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/.travis.yml b/.travis.yml index bf8f8d78..2fb30bf7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,31 @@ -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 + - openjdk8 + +matrix: + fast_finish: true + + include: + + allow_failures: 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 + - ./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/ -after_success: - - ./gradlew jacocoRootReport coveralls +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ diff --git a/README.adoc b/README.adoc index 40fec847..ecf403e2 100644 --- a/README.adoc +++ b/README.adoc @@ -1,19 +1,19 @@ = Functional Java -image:https://travis-ci.org/functionaljava/functionaljava.svg?branch=master["Build Status", link="https://travis-ci.org/functionaljava/functionaljava"] -image:https://coveralls.io/repos/github/functionaljava/functionaljava/badge.svg?branch=master["Coverage Status", link="https://coveralls.io/github/functionaljava/functionaljava?branch=master"] +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"] 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 provides abstractions for the following types: * Basic Data Structures - total and partial functions, products, unit, option, unbiased and right biased unions (either and validation), void. -* Immutable Collections - array, list, vector, stream, set, map, finger tree, heterogenous list, difference list. +* Immutable Collections - array, list, vector, stream, set, map, priority queue, finger tree, heterogenous list, difference list. * Other Abstractions - monoid, semigroup, natural, random number generator, reader, writer, state, input/output, parser, zipper, specification based testing (quickcheck), actors, optics (lens, prism, fold, traversal and others), concurrency and type conversion. == URLs @@ -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 @@ -32,15 +33,14 @@ 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`) -* Java 8 specific support (`functionaljava-java8`) * property based testing (`functionaljava-quickcheck`) +* a small amount of Java 8 support (`functionaljava-java-core`) -The latest stable version is `4.5`. 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.5" -compile "org.functionaljava:functionaljava-java8:4.5" -compile "org.functionaljava:functionaljava-quickcheck:4.5" -compile "org.functionaljava:functionaljava-java-core:4.5" +compile "org.functionaljava:functionaljava:5.0" +compile "org.functionaljava:functionaljava-quickcheck:5.0" +compile "org.functionaljava:functionaljava-java-core:5.0" ---- and in Maven: @@ -48,34 +48,27 @@ and in Maven: org.functionaljava functionaljava - 4.5 - - - org.functionaljava - functionaljava-java8 - 4.5 + 5.0 org.functionaljava functionaljava-quickcheck - 4.5 + 5.0 org.functionaljava functionaljava-java-core - 4.5 + 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 2.10. In the root directory run: +Building is done using Java 8 and 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 @@ -100,6 +93,7 @@ A more complete description of the features mentioned above are: ** Immutable set implementation using a red/black tree (`fj.data.Set`). ** Immutable multi-way tree - aka rose tree (`fj.data.Tree`). ** Immutable tree-map using a red/black tree implementation (`fj.data.TreeMap`). +** Immutable priority queue using finger trees (`fj.data.PriorityQueue`). ** Difference lists, a highly performant list. * Other Abstractions ** Monoid (`fj.Monoid`). @@ -121,3 +115,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 link:etc/release-notes. diff --git a/build.gradle b/build.gradle index 7a3b81cf..c89c9536 100644 --- a/build.gradle +++ b/build.gradle @@ -1,31 +1,26 @@ defaultTasks 'build' -ext { -} +apply plugin: "com.github.ben-manes.versions" buildscript { - ext { - uptodateVersion = "1.6.0" - retrolambdaVersion = "3.2.0" - } - repositories { mavenLocal() - jcenter() mavenCentral() + gradlePluginPortal() } dependencies { - classpath "com.ofg:uptodate-gradle-plugin:$uptodateVersion" - classpath "me.tatarka:gradle-retrolambda:$retrolambdaVersion" - classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.6.3' + classpath "com.github.ben-manes:gradle-versions-plugin:0.42.0" + classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.1.0" } -} + wrapper { + gradleVersion = "7.4" + distributionType = Wrapper.DistributionType.ALL + } +} -apply plugin: "jacoco" -apply plugin: 'com.github.kt3k.coveralls' if (JavaVersion.current().isJava8Compatible()) { allprojects { @@ -37,24 +32,24 @@ if (JavaVersion.current().isJava8Compatible()) { allprojects { - + apply plugin: "jacoco" jacoco { - toolVersion = "0.7.1.201405082137" + toolVersion = "0.8.7" } defaultTasks "build" - ext { + ext { isSnapshot = true - fjBaseVersion = "4.6" + fjBaseVersion = "5.1" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.5" + fjConsumeVersion = "5.0" signModule = false - useRetroLambda = false + uploadModule = false projectTitle = "Functional Java" projectName = "functionaljava" @@ -64,27 +59,32 @@ 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" - dependencyJunit = "junit:junit:4.12" + junitCompile = "junit:junit:4.13.2" + junitRuntime = "org.junit.vintage:junit-vintage-engine:5.8.2" displayCompilerWarnings = true - - newJdkEnvVar = "JAVA8_HOME" - oldJdkEnvVar = "JAVA6_HOME" - retroLambdaTarget = JavaVersion.VERSION_1_6 + generateTestReports = false } repositories { - jcenter() - mavenCentral() mavenLocal() + mavenCentral() } version = fjVersion @@ -97,68 +97,64 @@ subprojects { buildscript { repositories { + mavenLocal() mavenCentral() } } - apply plugin: "jacoco" - apply from: "$rootDir/lib.gradle" - apply plugin: "java" + apply plugin: "java-library" apply plugin: "eclipse" - apply plugin: "com.ofg.uptodate" repositories { mavenLocal() - jcenter() mavenCentral() maven { url sonatypeRepositoryUrl } } - if (displayCompilerWarnings) { - tasks.withType(JavaCompile) { + tasks.withType(JavaCompile) { + if (displayCompilerWarnings) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - } + } + } - jacocoTestReport { - reports { - html.enabled = true - xml.enabled = true - csv.enabled = false -// html.destination "${buildDir}/jacocoHtml" + tasks.withType(Test).configureEach { + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + if (!generateTestReports) { + reports.html.required = false + reports.junitXml.required = false } } - task coverage(dependsOn: ["test", "jacocoTestReport"]) << { - - } - } task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { - dependsOn = subprojects.test - sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs) - classDirectories = files(subprojects.sourceSets.main.output) - executionData = files(subprojects.jacocoTestReport.executionData) + dependsOn = subprojects*.test + executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") + // We only care about coverage of: + def projectForFoverage = ["core", "quickcheck", "java-core"] + 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 - xml.enabled = true - csv.enabled = false + html.required = true + xml.required = true } } configure(subprojects.findAll { it.name != "props-core" }) { - apply plugin: "maven" + apply plugin: "maven-publish" apply plugin: "signing" - apply plugin: "osgi" - + apply plugin: "biz.aQute.bnd.builder" sourceCompatibility = "1.8" + javadoc { + } + task javadocJar(type: Jar, dependsOn: "javadoc") { classifier = 'javadoc' from "build/docs/javadoc" @@ -176,16 +172,14 @@ 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+'"' - } - } + archiveVersion = project.fjVersion + 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 { @@ -197,7 +191,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")) } @@ -205,26 +199,6 @@ configure(subprojects.findAll { it.name != "props-core" }) { eclipseProject.dependsOn eclipsePluginManifest } -task env << { +task env doLast { println System.getenv() } - -task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { - dependsOn = subprojects.jacocoTestReport - sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs) - classDirectories = files(subprojects.sourceSets.main.output) - executionData = files(subprojects.jacocoTestReport.executionData) - reports { - xml.enabled true - xml.destination ="${buildDir}/reports/jacoco/test/jacocoTestReport.xml" - } - // We could remove the following setOnlyIf line, but then - // jacocoRootReport would silently be SKIPPED if something with - // the projectsWithUnitTests is wrong (e.g. a project is missing - // in there). - setOnlyIf { true } -} - -coveralls { - sourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs).files.absolutePath -} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..b7ddeec5 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,4 @@ +ignore: + - "demo/.*" + - "consume/.*" + - "performance/.*" diff --git a/consume/build.gradle b/consume/build.gradle index ff92a601..45dadefb 100644 --- a/consume/build.gradle +++ b/consume/build.gradle @@ -2,7 +2,8 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile("$group:$projectName:$fjConsumeVersion") + api "$group:$projectName:$fjConsumeVersion" - testCompile dependencyJunit + testImplementation junitCompile + testRuntimeOnly junitRuntime } 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/build.gradle b/core/build.gradle index 1a1763e7..282ec191 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,17 +1,18 @@ ext { signModule = true + uploadModule = true + } archivesBaseName = project.projectName dependencies { - testCompile dependencyJunit + 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 - -configureAllRetroLambda() +configureUpload(signingEnabled, signModule, uploadModule) diff --git a/core/src/main/java/fj/Bottom.java b/core/src/main/java/fj/Bottom.java index a9951928..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() { @@ -72,7 +70,7 @@ public static Error decons(final A a, final Show sa) { * @param c The type being deconstructed. * @return A deconstruction failure that was non-exhaustive. */ - @SuppressWarnings({"UnnecessaryFullyQualifiedName"}) + @SuppressWarnings("UnnecessaryFullyQualifiedName") public static Error decons(final java.lang.Class c) { return error("Deconstruction failure on type " + c); } @@ -83,7 +81,7 @@ public static Error decons(final java.lang.Class c) { * @return A function that returns the toString for a throwable. */ public static F eToString() { - return t -> t.toString(); + return Throwable::toString; } /** @@ -92,6 +90,6 @@ public static F eToString() { * @return A function that returns the getMessage for a throwable. */ public static F eMessage() { - return t -> t.getMessage(); + return Throwable::getMessage; } } diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java new file mode 100644 index 00000000..e100b6d6 --- /dev/null +++ b/core/src/main/java/fj/Bounded.java @@ -0,0 +1,50 @@ +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 final class Bounded { + + private final Definition def; + + /** + * Minimal definition of Bounded + */ + 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; + } + }); + } + +} diff --git a/core/src/main/java/fj/Class.java b/core/src/main/java/fj/Class.java index 6eac6e39..8bee1951 100644 --- a/core/src/main/java/fj/Class.java +++ b/core/src/main/java/fj/Class.java @@ -2,7 +2,6 @@ import fj.data.List; import static fj.data.List.unfold; -import fj.data.Option; import static fj.data.Option.none; import static fj.data.Option.some; import fj.data.Tree; @@ -12,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; @@ -39,14 +36,14 @@ public java.lang.Class _1() { return c2; } - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public java.lang.Class _2() { return c2.getSuperclass(); } }; return some(p); } - }, c).map(c1 -> clas(c1)); + }, c).map(Class::clas); } /** @@ -102,7 +99,7 @@ public static Tree typeParameterTree(final Type t) { } types = Tree.node(pt.getRawType(), typeArgs); } else { - types = Tree.node(t, List.>nil()); + types = Tree.node(t, List.nil()); } return types; } @@ -123,6 +120,6 @@ public java.lang.Class clas() { * @return A class from the given argument. */ public static Class clas(final java.lang.Class c) { - return new Class(c); + return new Class<>(c); } } diff --git a/core/src/main/java/fj/Digit.java b/core/src/main/java/fj/Digit.java index ba3c2245..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 { /** @@ -176,20 +174,20 @@ public static Option fromChar(final char c) { /** * First-class conversion from digit to a long. */ - public static final F toLong = d -> d.toLong(); + public static final F toLong = Digit::toLong; /** * First-class conversion from a long to a digit. */ - public static final F fromLong = i -> fromLong(i); + public static final F fromLong = Digit::fromLong; /** * First-class conversion from a digit to a character. */ - public static final F toChar = d -> d.toChar(); + public static final F toChar = Digit::toChar; /** * First-class conversion from a character to a digit. */ - public static final F> fromChar = c -> fromChar(c); + public static final F> fromChar = Digit::fromChar; } diff --git a/core/src/main/java/fj/Effect.java b/core/src/main/java/fj/Effect.java index aec3e442..2b5dcd6f 100644 --- a/core/src/main/java/fj/Effect.java +++ b/core/src/main/java/fj/Effect.java @@ -14,10 +14,8 @@ /** * Represents a side-effect. - * - * @version %build.number% */ -public class Effect { +public final class Effect { private Effect() {} @@ -33,7 +31,7 @@ public static P1 f(Effect0 e) { * * @return The function using the given effect. */ - public static final F f(Effect1 e1) { + public static F f(Effect1 e1) { return a -> { e1.f(a); return unit(); @@ -95,12 +93,12 @@ public static F5 f(Effect5 z * @param f The function to map over the effect. * @return An effect after a contra-variant map. */ - public static final Effect1 contramap(Effect1 e1, final F f) { + public static Effect1 contramap(Effect1 e1, final F f) { return b -> e1.f(f.f(b)); } public static Effect1 lazy(final F f) { - return a -> f.f(a); + return f::f; } diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 60fcb86d..cba4a589 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -1,9 +1,9 @@ package fj; -import static fj.Function.curry; - 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; @@ -11,20 +11,83 @@ import fj.data.vector.V6; import fj.data.vector.V7; import fj.data.vector.V8; +import fj.parser.Result; -import java.math.BigInteger; import java.math.BigDecimal; +import java.math.BigInteger; + +import static fj.Function.compose; +import static fj.Function.constant; +import static fj.Function.curry; /** * Tests for equality between two objects. - * - * @version %build.number% */ public final class Equal { - private final F> f; - private Equal(final F> f) { - this.f = f; + /** + * Primitives functions of Equal: minimal definition and overridable methods. + */ + public interface Definition { + + F equal(A a); + + 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); + } + } + + /** + * Primitives functions of Equal: alternative minimal definition and overridable methods. + */ + public interface AltDefinition extends Definition { + + @Override + boolean equal(A a1, A a2); + + @Override + default F equal(A a) { + return a2 -> equal(a, a2); + } + } + + private final Definition def; + + private Equal(final Definition def) { + this.def = def; } /** @@ -35,7 +98,7 @@ private Equal(final F> f) { * @return true if the two given arguments are equal, false otherwise. */ public boolean eq(final A a1, final A a2) { - return f.f(a1).f(a2); + return def.equal(a1, a2); } /** @@ -46,7 +109,7 @@ public boolean eq(final A a1, final A a2) { * @return true if the two given arguments are not equal, false otherwise. */ public boolean notEq(final A a1, final A a2) { - return !eq(a1, a2); + return !def.equal(a1, a2); } /** @@ -55,7 +118,7 @@ public boolean notEq(final A a1, final A a2) { * @return A function that returns true if the two given arguments are equal. */ public F2 eq() { - return (a, a1) -> eq(a, a1); + return def::equal; } /** @@ -65,7 +128,7 @@ public F2 eq() { * @return A function that returns true if the given argument equals the argument to this method. */ public F eq(final A a) { - return a1 -> eq(a, a1); + return def.equal(a); } /** @@ -75,17 +138,91 @@ public F eq(final A a) { * @return A new equal. */ public Equal contramap(final F f) { - return equal(F1Functions.o(F1Functions.o(F1Functions.andThen(f), this.f), f)); + return equalDef(contramapDef(f, def)); + } + + /** + * 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)); + } + + + 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); + } + + @Override + public boolean equal(B b1, B b2) { + return aEqDef.equal(f.f(b1), f.f(b2)); + } + }; + } + + /** + * Static version of {@link #contramap(F)} + */ + public static Equal contramap(final F f, final Equal eq) { + return eq.contramap(f); + } + + /** + * Begin definition of an equal instance. + * @see Definition#then(F, Equal) + */ + public static Definition on(final F f, final Equal eq) { + return contramapDef(f, eq.def); } /** * Constructs an equal instance from the given function. * + * Java 8+ users: use {@link #equalDef(Definition)} instead. + * * @param f The function to construct the equal with. * @return An equal instance from the given function. */ public static Equal equal(final F> f) { - return new Equal(f); + return new Equal<>(f::f); + } + + + /** + * Constructs an equal instance from the given function. + * + * Java 8+ users: use {@link #equalDef(AltDefinition)} instead. + * + * @param f The function to construct the equal with. + * @return An equal instance from the given function. + */ + public static Equal equal(final F2 f) { + return equalDef(f::f); + } + + /** + * Constructs an equal instance from the given definition. + * + * @param definition a definition of the equal instance. + * @return An equal instance from the given function. + */ + public static Equal equalDef(final Definition definition) { + return new Equal<>(definition); + } + + /** + * Constructs an equal instance from the given (alternative) definition. + * + * @param definition a definition of the equal instance. + * @return An equal instance from the given function. + */ + public static Equal equalDef(final AltDefinition definition) { + return new Equal<>(definition); } /** @@ -96,7 +233,17 @@ public static Equal equal(final F> f) { * equality. */ public static Equal anyEqual() { - return equal(a1 -> a2 -> a1.equals(a2)); + return equalDef(new Definition() { + @Override + public F equal(A a) { + return a::equals; + } + + @Override + public boolean equal(A a1, A a2) { + return a1.equals(a2); + } + }); } /** @@ -149,6 +296,11 @@ public static Equal anyEqual() { */ public static final Equal shortEqual = anyEqual(); + /** + * An equal instance for the Natural type. + */ + public static final Equal naturalEqual = bigintEqual.contramap(Natural::bigIntegerValue); + /** * An equal instance for the {@link String} type. */ @@ -158,7 +310,7 @@ public static Equal anyEqual() { * An equal instance for the {@link StringBuffer} type. */ public static final Equal stringBufferEqual = - equal(sb1 -> sb2 -> { + equalDef((sb1, sb2) -> { if (sb1.length() == sb2.length()) { for (int i = 0; i < sb1.length(); i++) if (sb1.charAt(i) != sb2.charAt(i)) @@ -172,7 +324,7 @@ public static Equal anyEqual() { * An equal instance for the {@link StringBuilder} type. */ public static final Equal stringBuilderEqual = - equal(sb1 -> sb2 -> { + equalDef((sb1, sb2) -> { if (sb1.length() == sb2.length()) { for (int i = 0; i < sb1.length(); i++) if (sb1.charAt(i) != sb2.charAt(i)) @@ -182,6 +334,11 @@ public static Equal anyEqual() { return false; }); + /** + * An equal instance for the {@link BitSet} type. + */ + public static final Equal bitSetSequal = equalDef((bs1, bs2) -> bs1.longValue() == bs2.longValue()); + /** * An equal instance for the {@link Either} type. * @@ -190,10 +347,29 @@ public static Equal anyEqual() { * @return An equal instance for the {@link Either} type. */ public static Equal> eitherEqual(final Equal ea, final Equal eb) { - return equal(e1 -> e2 -> e1.isLeft() && e2.isLeft() && ea.f.f(e1.left().value()).f(e2.left().value()) || - e1.isRight() && e2.isRight() && eb.f.f(e1.right().value()).f(e2.right().value())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + return equalDef(e1 -> e1.either( + a1 -> Either.either_(eaDef.equal(a1), (B __) -> false), + b1 -> Either.either_((A __)-> false, ebDef.equal(b1)) + )); + } + + 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; + return equalDef((r1, r2) -> eaDef.equal(r1.value(), r2.value()) && eiDef.equal(r1.rest(), r2.rest())); + } + + /** * An equal instance for the {@link Validation} type. * @@ -202,7 +378,7 @@ public static Equal> eitherEqual(final Equal ea, final Eq * @return An equal instance for the {@link Validation} type. */ public static Equal> validationEqual(final Equal ea, final Equal eb) { - return eitherEqual(ea, eb).contramap(Validation.either()); + return eitherEqual(ea, eb).contramap(Validation.either()); } /** @@ -212,12 +388,13 @@ public static Equal> validationEqual(final Equal ea, * @return An equal instance for the {@link List} type. */ public static Equal> listEqual(final Equal ea) { - return equal(a1 -> a2 -> { + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { List x1 = a1; List x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { - if (!ea.eq(x1.head(), x2.head())) + if (!eaDef.equal(x1.head(), x2.head())) return false; x1 = x1.tail(); @@ -235,7 +412,7 @@ public static Equal> listEqual(final Equal ea) { * @return An equal instance for the {@link NonEmptyList} type. */ public static Equal> nonEmptyListEqual(final Equal ea) { - return listEqual(ea).contramap(NonEmptyList.toList_()); + return listEqual(ea).contramap(NonEmptyList.toList_()); } /** @@ -245,12 +422,15 @@ public static Equal> nonEmptyListEqual(final Equal ea) { * @return An equal instance for the {@link Option} type. */ public static Equal> optionEqual(final Equal ea) { - return equal(o1 -> o2 -> o1.isNone() && o2.isNone() || - o1.isSome() && o2.isSome() && ea.f.f(o1.some()).f(o2.some())); + Definition eaDef = ea.def; + return equalDef(o1 -> o1.option( + Option.isNone_(), + a1 -> Option.option_(false, eaDef.equal(a1)) + )); } public static Equal> seqEqual(final Equal e) { - return equal(s1 -> s2 -> streamEqual(e).eq(s1.toStream(), s2.toStream())); + return streamEqual(e).contramap(Seq::toStream); } /** @@ -260,12 +440,13 @@ public static Equal> seqEqual(final Equal e) { * @return An equal instance for the {@link Stream} type. */ public static Equal> streamEqual(final Equal ea) { - return equal(a1 -> a2 -> { + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { Stream x1 = a1; Stream x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { - if (!ea.eq(x1.head(), x2.head())) + if (!eaDef.equal(x1.head(), x2.head())) return false; x1 = x1.tail()._1(); @@ -277,16 +458,51 @@ 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 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. * @return An equal instance for the {@link Array} type. */ public static Equal> arrayEqual(final Equal ea) { - return equal(a1 -> a2 -> { + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { if (a1.length() == a2.length()) { for (int i = 0; i < a1.length(); i++) { - if (!ea.eq(a1.get(i), a2.get(i))) + if (!eaDef.equal(a1.get(i), a2.get(i))) return false; } return true; @@ -302,7 +518,18 @@ public static Equal> arrayEqual(final Equal ea) { * @return An equal instance for the {@link Tree} type. */ public static Equal> treeEqual(final Equal ea) { - return Equal.>equal(curry((t1, t2) -> ea.eq(t1.root(), t2.root()) && p1Equal(streamEqual(Equal.treeEqual(ea))).eq(t2.subForest(), t1.subForest()))); + Definition eaDef = ea.def; + return equalDef(new AltDefinition>() { + + final Definition>>> subForestEqDef = p1Equal(streamEqual(equalDef(this))).def; + + @Override + public boolean equal(Tree t1, Tree t2) { + return eaDef.equal(t1.root(), t2.root()) + && subForestEqDef.equal(t1.subForest(), t2.subForest()); + + } + }); } /** @@ -312,7 +539,7 @@ public static Equal> treeEqual(final Equal ea) { * @return An equal instance for a product-1. */ public static Equal> p1Equal(final Equal ea) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1())); + return ea.contramap(P1.__1()); } /** @@ -323,7 +550,9 @@ public static Equal> p1Equal(final Equal ea) { * @return An equal instance for a product-2. */ public static Equal> p2Equal(final Equal ea, final Equal eb) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + return equalDef((p1, p2)-> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2())); } /** @@ -335,7 +564,10 @@ public static Equal> p2Equal(final Equal ea, final Equal e * @return An equal instance for a product-3. */ public static Equal> p3Equal(final Equal ea, final Equal eb, final Equal ec) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3())); } /** @@ -349,8 +581,12 @@ public static Equal> p3Equal(final Equal ea, final Equa */ public static Equal> p4Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && - ed.eq(p1._4(), p2._4())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + Definition edDef = ed.def; + return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && + edDef.equal(p1._4(), p2._4())); } /** @@ -366,8 +602,13 @@ public static Equal> p4Equal(final Equal ea, fina public static Equal> p5Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && - ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + Definition edDef = ed.def; + Definition eeDef = ee.def; + return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && + edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5())); } /** @@ -384,8 +625,14 @@ public static Equal> p5Equal(final Equal ea public static Equal> p6Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee, final Equal ef) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && - ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + Definition edDef = ed.def; + Definition eeDef = ee.def; + Definition efDef = ef.def; + return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && + edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(p1._6(), p2._6())); } /** @@ -404,9 +651,16 @@ public static Equal> p5Equal(final Equal ea final Equal ec, final Equal ed, final Equal ee, final Equal ef, final Equal eg) { - return equal(p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && - ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6()) && - eg.eq(p1._7(), p2._7())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + Definition edDef = ed.def; + Definition eeDef = ee.def; + Definition efDef = ef.def; + Definition egDef = eg.def; + return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && + edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(p1._6(), p2._6()) && + egDef.equal(p1._7(), p2._7())); } /** @@ -430,10 +684,18 @@ public static Equal> p5Equal(final Equal ea final Equal ef, final Equal eg, final Equal eh) { - return equal( - p1 -> p2 -> ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && - ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6()) && - eg.eq(p1._7(), p2._7()) && eh.eq(p1._8(), p2._8())); + Definition eaDef = ea.def; + Definition ebDef = eb.def; + Definition ecDef = ec.def; + Definition edDef = ed.def; + Definition eeDef = ee.def; + Definition efDef = ef.def; + Definition egDef = eg.def; + Definition ehDef = eh.def; + return equalDef( + (p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && + edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(p1._6(), p2._6()) && + egDef.equal(p1._7(), p2._7()) && ehDef.equal(p1._8(), p2._8())); } /** @@ -443,7 +705,7 @@ public static Equal> p5Equal(final Equal ea * @return An equal instance for a vector-2. */ public static Equal> v2Equal(final Equal ea) { - return streamEqual(ea).contramap(V2.toStream_()); + return streamEqual(ea).contramap(V2.toStream_()); } /** @@ -453,7 +715,7 @@ public static Equal> v2Equal(final Equal ea) { * @return An equal instance for a vector-3. */ public static Equal> v3Equal(final Equal ea) { - return streamEqual(ea).contramap(V3.toStream_()); + return streamEqual(ea).contramap(V3.toStream_()); } /** @@ -463,7 +725,7 @@ public static Equal> v3Equal(final Equal ea) { * @return An equal instance for a vector-4. */ public static Equal> v4Equal(final Equal ea) { - return streamEqual(ea).contramap(V4.toStream_()); + return streamEqual(ea).contramap(V4.toStream_()); } /** @@ -473,7 +735,7 @@ public static Equal> v4Equal(final Equal ea) { * @return An equal instance for a vector-5. */ public static Equal> v5Equal(final Equal ea) { - return streamEqual(ea).contramap(V5.toStream_()); + return streamEqual(ea).contramap(V5.toStream_()); } /** @@ -483,7 +745,7 @@ public static Equal> v5Equal(final Equal ea) { * @return An equal instance for a vector-6. */ public static Equal> v6Equal(final Equal ea) { - return streamEqual(ea).contramap(V6.toStream_()); + return streamEqual(ea).contramap(V6.toStream_()); } /** @@ -493,7 +755,7 @@ public static Equal> v6Equal(final Equal ea) { * @return An equal instance for a vector-7. */ public static Equal> v7Equal(final Equal ea) { - return streamEqual(ea).contramap(V7.toStream_()); + return streamEqual(ea).contramap(V7.toStream_()); } /** @@ -503,13 +765,13 @@ public static Equal> v7Equal(final Equal ea) { * @return An equal instance for a vector-8. */ public static Equal> v8Equal(final Equal ea) { - return streamEqual(ea).contramap(V8.toStream_()); + return streamEqual(ea).contramap(V8.toStream_()); } /** * 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. @@ -524,7 +786,9 @@ public static Equal> v8Equal(final Equal ea) { * @return an equal instance for a heterogeneous list. */ public static > Equal> hListEqual(final Equal e, final Equal l) { - return equal(curry((HList.HCons c1, HList.HCons c2) -> e.eq(c1.head(), c2.head()) && l.eq(c1.tail(), c2.tail()))); + Definition eDef = e.def; + Definition lDef = l.def; + return equalDef((c1, c2) -> eDef.equal(c1.head(), c2.head()) && lDef.equal(c1.tail(), c2.tail())); } /** @@ -534,34 +798,15 @@ public static > Equal> hListEqual(final * @return An equal instance for sets. */ public static Equal> setEqual(final Equal e) { - return equal(curry((Set a, Set b) -> streamEqual(e).eq(a.toStream(), b.toStream()))); + return streamEqual(e).contramap(Set::toStream); } public static Equal> treeMapEqual(Equal k, Equal v) { - return equal(t1 -> t2 -> Equal.streamEqual(p2Equal(k, v)).eq(t1.toStream(), t2.toStream())); + return streamEqual(p2Equal(k, v)).contramap(TreeMap::toStream); } public static Equal> writerEqual(Equal eq1, Equal eq2) { - return equal(w1 -> w2 -> p2Equal(eq1, eq2).eq(w1.run(), w2.run())); - } - - /** - * @return Returns none if no equality can be determined by checking the nullity and reference values, else the equality - * @deprecated see issue #122. - */ - @Deprecated - public static Option shallowEqualsO(Object o1, Object o2) { - if (o1 == null && o2 == null) { - return Option.some(true); - } else if (o1 == o2) { - return Option.some(true); - } else if (o1 != null && o2 != null) { - java.lang.Class c = o1.getClass(); - // WARNING: this may return some(false) for two instance of same type (and thus comparable) but of different class (typicaly anonymous class instance). - return c.isInstance(o2) ? Option.none() : Option.some(false); - } else { - return Option.some(false); - } + return p2Equal(eq1, eq2).contramap(Writer::run); } /** diff --git a/core/src/main/java/fj/F.java b/core/src/main/java/fj/F.java index 1b524fdc..1736f97b 100644 --- a/core/src/main/java/fj/F.java +++ b/core/src/main/java/fj/F.java @@ -3,48 +3,693 @@ import fj.control.parallel.Actor; import fj.control.parallel.Promise; import fj.control.parallel.Strategy; -import fj.data.Array; -import fj.data.Either; -import fj.data.IterableW; -import fj.data.List; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Set; -import fj.data.Stream; -import fj.data.Tree; -import fj.data.TreeZipper; -import fj.data.Validation; -import fj.data.Zipper; +import fj.data.*; import java.util.ArrayList; import java.util.LinkedList; -import java.util.PriorityQueue; import java.util.TreeSet; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.SynchronousQueue; +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% + * A transformation or function from A to B. */ -public interface F { +@FunctionalInterface +public interface F extends Function { /** * Transform A to B. * * @param a The A to transform. * @return The result of the transformation. */ - public abstract B f(A a); + 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/F0.java b/core/src/main/java/fj/F0.java index e85689da..88d60a9e 100644 --- a/core/src/main/java/fj/F0.java +++ b/core/src/main/java/fj/F0.java @@ -1,10 +1,34 @@ package fj; -/** - * Created by MarkPerry on 21/01/2015. - */ -public interface F0 { +import fj.function.Effect0; +import fj.function.Try0; +import fj.function.TryEffect0; - public A f(); +import java.util.function.Supplier; + +@FunctionalInterface +public interface F0 extends Supplier { + + A f(); + + 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/F1Functions.java b/core/src/main/java/fj/F1Functions.java deleted file mode 100644 index 2d3c56d4..00000000 --- a/core/src/main/java/fj/F1Functions.java +++ /dev/null @@ -1,677 +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 class F1Functions { - - - /** - * Function composition - * - * @param g A function to compose with this one. - * @return The composed function such that this function is applied last. - */ - static public 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. - */ - static public 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"}) - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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"}) - static public 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"}) - static public 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"}) - static public 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"}) - static public 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. - */ - static public 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. - */ - static public 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"}) - static public 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"}) - static public F, IterableW> mapIterable(final F f) { - return F1Functions.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"}) - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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"}) - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public ArrayList mapJ(final F f, final ArrayList as) { - return new ArrayList(iterableStream(as).map(f).toCollection()); - } - - static public F map(F target, F f) { - return andThen(target, f); - } - - static public F contramap(F target, F f) { - return andThen(f, target); - } - -} diff --git a/core/src/main/java/fj/F1W.java b/core/src/main/java/fj/F1W.java deleted file mode 100644 index 73545411..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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ArrayList mapJ(final ArrayList as) { - return F1Functions.mapJ(this, as); - } - - public F1W map(F f) { - return lift(F1Functions.map(this, f)); - } - - public 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 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 0aa82585..8b1aac20 100644 --- a/core/src/main/java/fj/F2.java +++ b/core/src/main/java/fj/F2.java @@ -1,31 +1,22 @@ package fj; import fj.control.parallel.Promise; -import fj.data.Array; -import fj.data.IterableW; -import fj.data.List; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Set; -import fj.data.Stream; -import fj.data.Tree; -import fj.data.TreeZipper; -import fj.data.Zipper; +import fj.data.*; + +import java.util.function.BiFunction; -import static fj.data.Tree.node; 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 { +@FunctionalInterface +public interface F2 extends BiFunction { /** * Transform A and B to C. * @@ -33,6 +24,266 @@ public interface F2 { * @param b The B to transform. * @return The result of the transformation. */ - public C f(A a, B b); + 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 c15a68cb..00000000 --- a/core/src/main/java/fj/F2Functions.java +++ /dev/null @@ -1,270 +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 class F2Functions { - - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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. - */ - static public 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())); - }; - } - - static public F2 contramapFirst(F2 target, F f) { - return (z, b) -> target.f(f.f(z), b); - } - - static public F2 contramapSecond(F2 target, F f) { - return (a, z) -> target.f(a, f.f(z)); - } - - static public F2 contramap(F2 target, F f, F g) { - return contramapSecond(contramapFirst(target, f), g); - } - - static public 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 7a2f6e19..00000000 --- a/core/src/main/java/fj/F2W.java +++ /dev/null @@ -1,261 +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 F2W, TreeZipper, TreeZipper> zipTreeZipperM() { - return lift(F2Functions.zipTreeZipperM(this)); - } - - public F2W contramapFirst(F f) { - return lift(F2Functions.contramapFirst(this, f)); - } - - public F2W contramapSecond(F f) { - return lift(F2Functions.contramapSecond(this, f)); - } - - public F2W contramap(F f, F g) { - return lift(F2Functions.contramap(this, f, g)); - } - - public 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 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/F3.java b/core/src/main/java/fj/F3.java index 68e7627a..b7efeb3a 100644 --- a/core/src/main/java/fj/F3.java +++ b/core/src/main/java/fj/F3.java @@ -2,10 +2,9 @@ /** * 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 { /** * Transform A, B and C to D. @@ -15,5 +14,5 @@ public interface F3 { * @param c The C to transform. * @return The result of the transformation. */ - public D f(A a, B b, C c); + D f(A a, B b, C c); } diff --git a/core/src/main/java/fj/F3Functions.java b/core/src/main/java/fj/F3Functions.java deleted file mode 100644 index 62d91aee..00000000 --- a/core/src/main/java/fj/F3Functions.java +++ /dev/null @@ -1,25 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try3; - -import static fj.data.Validation.fail; -import static fj.data.Validation.success; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F3Functions { - - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/F4.java b/core/src/main/java/fj/F4.java index 9447389a..c3c5f882 100644 --- a/core/src/main/java/fj/F4.java +++ b/core/src/main/java/fj/F4.java @@ -2,10 +2,9 @@ /** * 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 { /** * Transform A, B, C and D to E. @@ -16,5 +15,5 @@ public interface F4 { * @param d The D to transform. * @return The result of the transformation. */ - public E f(A a, B b, C c, D d); + E f(A a, B b, C c, D d); } diff --git a/core/src/main/java/fj/F4Functions.java b/core/src/main/java/fj/F4Functions.java deleted file mode 100644 index 71ee56a3..00000000 --- a/core/src/main/java/fj/F4Functions.java +++ /dev/null @@ -1,21 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try4; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F4Functions { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/F5.java b/core/src/main/java/fj/F5.java index ff7184f2..574afb15 100644 --- a/core/src/main/java/fj/F5.java +++ b/core/src/main/java/fj/F5.java @@ -2,11 +2,9 @@ /** * 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 { /** * Transform A, B, C, D and E to @@ -19,5 +17,5 @@ public interface F5 { * @param e The E to transform. * @return The result of the transformation. */ - public F$ f(A a, B b, C c, D d, E e); + F$ f(A a, B b, C c, D d, E e); } diff --git a/core/src/main/java/fj/F5Functions.java b/core/src/main/java/fj/F5Functions.java deleted file mode 100644 index 8cb26c6d..00000000 --- a/core/src/main/java/fj/F5Functions.java +++ /dev/null @@ -1,21 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try5; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F5Functions { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/F6.java b/core/src/main/java/fj/F6.java index f403eb0a..135e28fe 100644 --- a/core/src/main/java/fj/F6.java +++ b/core/src/main/java/fj/F6.java @@ -2,11 +2,9 @@ /** * 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 { /** * Transform A, B, C, D, E and @@ -20,5 +18,5 @@ public interface F6 { * @param f The F$ to transform. * @return The result of the transformation. */ - public G f(A a, B b, C c, D d, E e, F$ f); + G f(A a, B b, C c, D d, E e, F$ f); } diff --git a/core/src/main/java/fj/F6Functions.java b/core/src/main/java/fj/F6Functions.java deleted file mode 100644 index 70458593..00000000 --- a/core/src/main/java/fj/F6Functions.java +++ /dev/null @@ -1,22 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try6; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F6Functions { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/F7.java b/core/src/main/java/fj/F7.java index be9d87e1..a2542700 100644 --- a/core/src/main/java/fj/F7.java +++ b/core/src/main/java/fj/F7.java @@ -2,11 +2,9 @@ /** * 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 { /** * Transform A, B, C, D, E, @@ -21,5 +19,5 @@ public interface F7 { * @param g The G to transform. * @return The result of the transformation. */ - public H f(A a, B b, C c, D d, E e, F$ f, G g); + H f(A a, B b, C c, D d, E e, F$ f, G g); } diff --git a/core/src/main/java/fj/F7Functions.java b/core/src/main/java/fj/F7Functions.java deleted file mode 100644 index a6ceff9f..00000000 --- a/core/src/main/java/fj/F7Functions.java +++ /dev/null @@ -1,22 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try7; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F7Functions { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/F8.java b/core/src/main/java/fj/F8.java index 5ad75489..647fc14c 100644 --- a/core/src/main/java/fj/F8.java +++ b/core/src/main/java/fj/F8.java @@ -3,10 +3,9 @@ /** * 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 { /** * Transform A, B, C, D, E, @@ -22,5 +21,5 @@ public interface F8 { * @param h The H to transform. * @return The result of the transformation. */ - public I f(A a, B b, C c, D d, E e, F$ f, G g, H h); + I 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/F8Functions.java b/core/src/main/java/fj/F8Functions.java deleted file mode 100644 index 788f106d..00000000 --- a/core/src/main/java/fj/F8Functions.java +++ /dev/null @@ -1,21 +0,0 @@ -package fj; - -import fj.data.Validation; -import fj.function.Try8; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public class F8Functions { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - static public 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/main/java/fj/Function.java b/core/src/main/java/fj/Function.java index 4d4b14a2..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() { @@ -60,7 +58,7 @@ public static F> compose2(final F f, final F F, F, F>> andThen() { - return g -> f -> Function.andThen(g, f); + return g -> f -> andThen(g, f); } /** @@ -89,7 +87,7 @@ public static F identity() { * @return A function that given an argument, returns a function that ignores its argument. */ public static F> constant() { - return b -> constant(b); + return Function::constant; } /** @@ -109,7 +107,7 @@ public static F constant(final B b) { * @return A co- and contravariant function that invokes f on its argument. */ public static F vary(final F f) { - return a -> f.f(a); + return f::f; } /** @@ -118,7 +116,7 @@ public static F vary(final F f) { * @return A function that varies and covaries a function. */ public static F, F> vary() { - return f -> Function.vary(f); + return Function::vary; } /** @@ -127,7 +125,7 @@ public static F, F> vary() { * @return A function that takes a function and flips its arguments. */ public static F>, F>> flip() { - return f -> flip(f); + return Function::flip; } /** @@ -156,7 +154,7 @@ public static F2 flip(final F2 f) { * @return A function that flips the arguments of a given function. */ public static F, F2> flip2() { - return f -> flip(f); + return Function::flip; } /** @@ -168,7 +166,7 @@ public static F, F2> flip2() { * not apply the value, instead returning an empty optional value. */ public static F> nullable(final F f) { - return a -> a == null ? Option.none() : Option.some(f.f(a)); + return a -> a == null ? Option.none() : Option.some(f.f(a)); } /** @@ -198,7 +196,7 @@ public static F curry(final F2 f, final A a) { * @return An uncurried function. */ public static F>, F2> uncurryF2() { - return f -> uncurryF2(f); + return Function::uncurryF2; } /** @@ -250,7 +248,7 @@ public static F curry(final F3 f, final A a, fina * @return An uncurried function. */ public static F>>, F3> uncurryF3() { - return f -> uncurryF3(f); + return Function::uncurryF3; } /** @@ -315,7 +313,7 @@ public static F curry(final F4 f, final A a * @return An uncurried function. */ public static F>>>, F4> uncurryF4() { - return f -> uncurryF4(f); + return Function::uncurryF4; } /** @@ -396,7 +394,7 @@ public static F4 uncurryF4(final F F>>>>, F5> uncurryF5() { - return f -> uncurryF5(f); + return Function::uncurryF5; } /** @@ -425,7 +423,7 @@ public static F4 uncurryF4(final F F>>>>>, F6> uncurryF6() { - return f -> uncurryF6(f); + return Function::uncurryF6; } /** @@ -543,7 +541,7 @@ public static F4 uncurryF4(final F F>>>>>>, F7> uncurryF7() { - return f -> uncurryF7(f); + return Function::uncurryF7; } /** @@ -682,7 +680,7 @@ public static F4 uncurryF4(final F F>>>>>>>, F8> uncurryF8() { - return f -> uncurryF8(f); + return Function::uncurryF8; } /** @@ -718,7 +716,18 @@ public static F bind(final F ma, final F> f) { * @return A new function after applying the given higher-order function to the given function. */ public static F apply(final F> cab, final F ca) { - return bind(cab, f -> compose(f, ca)); + return apply(uncurryF2(cab), ca); + } + + /** + * Performs function application within a higher-order function (applicative functor pattern). + * + * @param cab The higher-order function to apply a function to. + * @param ca A function to apply within a higher-order function. + * @return A new function after applying the given higher-order function to the given function. + */ + public static F apply(final F2 cab, final F ca) { + return c -> cab.f(c, ca.f(c)); } /** @@ -761,17 +770,17 @@ public static F, F, F>> lift(final Ff. */ public static F join(final F> f) { - return bind(f, Function.>identity()); + return bind(f, Function.identity()); } /** * 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); @@ -779,11 +788,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); @@ -791,11 +800,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); @@ -803,11 +812,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) { @@ -816,11 +825,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$) { @@ -829,11 +838,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) { @@ -842,11 +851,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/Hash.java b/core/src/main/java/fj/Hash.java index 26f42049..241bcefb 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -10,14 +10,13 @@ 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; /** * Produces a hash code for an object which should attempt uniqueness. - * - * @version %build.number% */ public final class Hash { private final F f; @@ -62,7 +61,7 @@ public static Hash hash(final F f) { * @return A hash that uses {@link Object#hashCode()}. */ public static Hash anyHash() { - return hash(a -> a.hashCode()); + return hash(Object::hashCode); } /** @@ -115,6 +114,11 @@ public static Hash anyHash() { */ public static final Hash bigdecimalHash = anyHash(); + /** + * A hash instance for the {@link Natural} type. + */ + public static final Hash naturalHash = bigintHash.contramap(Natural::bigIntegerValue); + /** * A hash instance for the String type. */ @@ -157,6 +161,30 @@ 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. + * @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. * @@ -165,7 +193,7 @@ public static Hash> eitherHash(final Hash ha, final Hash< * @return A hash instance for the {@link Validation} type. */ public static Hash> validationHash(final Hash ha, final Hash hb) { - return eitherHash(ha, hb).contramap(Validation.either()); + return eitherHash(ha, hb).contramap(Validation.either()); } /** @@ -196,7 +224,7 @@ public static Hash> listHash(final Hash ha) { * @return A hash instance for the {@link NonEmptyList} type. */ public static Hash> nonEmptyListHash(final Hash ha) { - return listHash(ha).contramap(NonEmptyList.toList_()); + return listHash(ha).contramap(NonEmptyList.toList_()); } /** @@ -258,17 +286,61 @@ 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 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. * @return A hash instance for the {@link Tree} type. */ public static Hash> treeHash(final Hash ha) { - return streamHash(ha).contramap(Tree.flatten_()); + return streamHash(ha).contramap(Tree.flatten_()); } public static Hash> treeMapHash(final Hash h, final Hash v) { - return hash(t -> streamHash(Hash.p2Hash(h, v)).hash(t.toStream())); + return hash(t -> streamHash(p2Hash(h, v)).hash(t.toStream())); } /** @@ -278,7 +350,7 @@ public static Hash> treeMapHash(final Hash h, final Hash * @return A hash instance for a product-1. */ public static Hash> p1Hash(final Hash ha) { - return ha.contramap(P1.__1()); + return ha.contramap(P1.__1()); } /** @@ -473,7 +545,7 @@ public static Hash> p5Hash(final Hash ha, f * @return A hash instance for a vector-2. */ public static Hash> v2Hash(final Hash ea) { - return streamHash(ea).contramap(V2.toStream_()); + return streamHash(ea).contramap(V2.toStream_()); } /** @@ -483,7 +555,7 @@ public static Hash> v2Hash(final Hash ea) { * @return A hash instance for a vector-3. */ public static Hash> v3Hash(final Hash ea) { - return streamHash(ea).contramap(V3.toStream_()); + return streamHash(ea).contramap(V3.toStream_()); } /** @@ -493,7 +565,7 @@ public static Hash> v3Hash(final Hash ea) { * @return A hash instance for a vector-4. */ public static Hash> v4Hash(final Hash ea) { - return streamHash(ea).contramap(V4.toStream_()); + return streamHash(ea).contramap(V4.toStream_()); } /** @@ -503,7 +575,7 @@ public static Hash> v4Hash(final Hash ea) { * @return A hash instance for a vector-5. */ public static Hash> v5Hash(final Hash ea) { - return streamHash(ea).contramap(V5.toStream_()); + return streamHash(ea).contramap(V5.toStream_()); } /** @@ -513,7 +585,7 @@ public static Hash> v5Hash(final Hash ea) { * @return A hash instance for a vector-6. */ public static Hash> v6Hash(final Hash ea) { - return streamHash(ea).contramap(V6.toStream_()); + return streamHash(ea).contramap(V6.toStream_()); } /** @@ -523,7 +595,7 @@ public static Hash> v6Hash(final Hash ea) { * @return A hash instance for a vector-7. */ public static Hash> v7Hash(final Hash ea) { - return streamHash(ea).contramap(V7.toStream_()); + return streamHash(ea).contramap(V7.toStream_()); } /** @@ -533,6 +605,7 @@ public static Hash> v7Hash(final Hash ea) { * @return A hash instance for a vector-8. */ public static Hash> v8Hash(final Hash ea) { - return streamHash(ea).contramap(V8.toStream_()); + return streamHash(ea).contramap(V8.toStream_()); } + } diff --git a/core/src/main/java/fj/LcgRng.java b/core/src/main/java/fj/LcgRng.java index d2c244b6..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 { @@ -17,18 +15,18 @@ public LcgRng(long s) { seed = s; } - public long getSeed() { + public final long getSeed() { return seed; } - public P2 nextInt() { + public final P2 nextInt() { P2 p = nextLong(); int i = (int) p._2().longValue(); return P.p(p._1(), i); } - public P2 nextLong() { + public final P2 nextLong() { P2 p = nextLong(seed); return P.p(new LcgRng(p._1()), p._2()); } @@ -40,7 +38,7 @@ public P2 nextLong() { */ static P2 nextLong(long seed) { long newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL; - long n = (Long) (newSeed >>> 16); + long n = newSeed >>> 16; return P.p(newSeed, n); } diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 40d6e8e7..bc05f33e 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1,15 +1,14 @@ package fj; -import static fj.Function.curry; -import static fj.Function.flip; -import fj.data.Array; -import fj.data.List; -import fj.data.Natural; -import fj.data.Option; -import fj.data.Set; -import fj.data.Stream; - -import static fj.Semigroup.multiply1p; +import fj.data.*; + +import static fj.Function.*; +import static fj.Semigroup.semigroupDef; +import static fj.Unit.unit; +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; @@ -23,24 +22,92 @@ *
  • Right Identity; forall x. sum(x, zero()) == x
  • *
  • Associativity; forall x y z. sum(sum(x, y), z) == sum(x, sum(y, z))
  • * - * - * @version %build.number% */ public final class Monoid
    { - private final F> sum; - private final A zero; - private Monoid(final F> sum, final A zero) { - this.sum = sum; - this.zero = zero; + + private final Definition def; + + /** + * Primitives functions of Monoid: minimal definition and overridable methods. + */ + public interface Definition extends Semigroup.Definition { + + A empty(); + + + default A sum(F0> as) { + return as.f().foldLeft(this::append, empty()); + } + + @Override + default A sum(A a, F0> as) { + return sum(() -> Stream.cons(a, as)); + } + + default A multiply(int n, A a) { + return (n <= 0) + ? empty() + : Semigroup.Definition.super.multiply1p(n - 1, a); + } + + @Override + default A multiply1p(int n, A a) { + return n == Integer.MAX_VALUE + ? append(a, multiply(n, a)) + : multiply(n + 1, a); + } + + default Definition dual() { + return new Definition(){ + + @Override + public A empty() { + return Definition.this.empty(); + } + + @Override + public A append(A a1, A a2) { + return Definition.this.append(a2, a1); + } + + @Override + public A multiply(int n, A a) { + return Definition.this.multiply(n, a); + } + + @Override + public Definition dual() { + return Definition.this; + } + }; + } + } + + + /** + * Primitives functions of Monoid: alternative minimal definition and overridable methods. + */ + public interface AltDefinition extends Definition { + + @Override + F prepend(A a); + + @Override + default A append(A a1, A a2) { + return prepend(a1).f(a2); + } + } + + private Monoid(Definition def) { + this.def = def; } /** * Composes this monoid with another. */ public Monoid>compose(Monoid m) { - return monoid((P2 x) -> (P2 y) -> - P.p(sum(x._1(), y._1()), m.sum(x._2(), y._2())), P.p(zero, m.zero)); + return compose(m, P2.__1(), P2.__2(), P::p); } /** @@ -49,7 +116,81 @@ private Monoid(final F> sum, final A zero) { * @return A semigroup projection of this monoid. */ public Semigroup semigroup() { - return Semigroup.semigroup(sum); + return semigroupDef(def); + } + + /** + * Maps the given functions across this monoid as an invariant functor. + * + * @param f The covariant map. + * @param g The contra-variant map. + * @return A new monoid. + */ + public Monoid xmap(final F f, final F g) { + Monoid.Definition def = this.def; + B zero = f.f(def.empty()); + return monoidDef(new Definition() { + @Override + public B empty() { + return zero; + } + + @Override + public B append(B a1, B a2) { + return f.f(def.append(g.f(a1), g.f(a2))); + } + + @Override + public F prepend(B b) { + return def.prepend(g.f(b)).dimap(g, f); + } + + @Override + public B multiply(int n, B b) { + return f.f(def.multiply(n , g.f(b))); + } + + @Override + public B sum(F0> as) { + return f.f(def.sum(() -> as.f().map(g))); + } + }); + } + + + public Monoid compose(Monoid mb, final F a, final F b, final F2 c) { + Definition maDef = this.def; + Definition mbDef = mb.def; + C empty = c.f(maDef.empty(), mbDef.empty()); + return monoidDef(new Definition() { + + @Override + public C empty() { + return empty; + } + + @Override + public C append(C c1, C c2) { + return c.f(maDef.append(a.f(c1), a.f(c2)), mbDef.append(b.f(c1), b.f(c2))); + } + + @Override + public F prepend(C c1) { + F prependA = maDef.prepend(a.f(c1)); + F prependB = mbDef.prepend(b.f(c1)); + return c2 -> c.f(prependA.f(a.f(c2)), prependB.f(b.f(c2))); + } + + @Override + public C multiply(int n, C c1) { + return c.f(maDef.multiply(n, a.f(c1)), mbDef.multiply(n, b.f(c1))); + } + + @Override + public C sum(F0> cs) { + return c.f(maDef.sum(() -> cs.f().map(a)), mbDef.sum(() -> cs.f().map(b))); + } + }); } /** @@ -60,7 +201,7 @@ public Semigroup semigroup() { * @return The of the two given arguments. */ public A sum(final A a1, final A a2) { - return sum.f(a1).f(a2); + return def.append(a1, a2); } /** @@ -70,7 +211,7 @@ public A sum(final A a1, final A a2) { * @return A function that sums the given value according to this monoid. */ public F sum(final A a1) { - return sum.f(a1); + return def.prepend(a1); } /** @@ -79,7 +220,7 @@ public F sum(final A a1) { * @return A function that sums according to this monoid. */ public F> sum() { - return sum; + return def::prepend; } /** @@ -88,13 +229,13 @@ public F> sum() { * @return The zero value for this monoid. */ public A zero() { - return zero; + return def.empty(); } /** * 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 @@ -103,9 +244,7 @@ public A zero() { * {@code zero()} */ public A multiply(final int n, final A a) { - return (n <= 0) - ? zero - : multiply1p(sum, n - 1, a); + return def.multiply(n, a); } /** @@ -115,7 +254,7 @@ public A multiply(final int n, final A a) { * @return The sum of the given values. */ public A sumRight(final List as) { - return as.foldRight(sum, zero); + return as.foldRight(def::append, def.empty()); } /** @@ -125,7 +264,7 @@ public A sumRight(final List as) { * @return The sum of the given values. */ public A sumRight(final Stream as) { - return as.foldRight((a, ap1) -> sum(a, ap1._1()), zero); + return as.foldRight1(def::append, def.empty()); } /** @@ -135,7 +274,7 @@ public A sumRight(final Stream as) { * @return The sum of the given values. */ public A sumLeft(final List as) { - return as.foldLeft(sum, zero); + return as.foldLeft(def::append, def.empty()); } /** @@ -145,7 +284,7 @@ public A sumLeft(final List as) { * @return The sum of the given values. */ public A sumLeft(final Stream as) { - return as.foldLeft(sum, zero); + return def.sum(() -> as); } /** @@ -154,7 +293,7 @@ public A sumLeft(final Stream as) { * @return a function that sums the given values with left-fold. */ public F, A> sumLeft() { - return as -> sumLeft(as); + return this::sumLeft; } /** @@ -163,7 +302,7 @@ public F, A> sumLeft() { * @return a function that sums the given values with right-fold. */ public F, A> sumRight() { - return as -> sumRight(as); + return this::sumRight; } /** @@ -172,7 +311,7 @@ public F, A> sumRight() { * @return a function that sums the given values with left-fold. */ public F, A> sumLeftS() { - return as -> sumLeft(as); + return this::sumLeft; } /** @@ -184,140 +323,468 @@ public F, A> sumLeftS() { */ public A join(final Iterable as, final A a) { final Stream s = iterableStream(as); - return s.isEmpty() ? - zero : - s.foldLeft1(Function.compose(sum, flip(sum).f(a))); + F prependA = def.prepend(a); + return s.isEmpty() + ? def.empty() + : s.foldLeft1((a1, a2) -> def.append(a1, prependA.f(a2))); } /** - * Constructs a monoid from the given sum function and zero value, which must follow the monoidal + * Swaps the arguments when summing. + */ + public Monoid dual() { + return monoidDef(def.dual()); + } + + /** + * Constructs a monoid from the given definition, which must follow the monoidal * laws. * - * @param sum The sum function for the monoid. + * @param def The definition for the monoid. + * @return A monoid instance that uses the given sun function and zero value. + */ + public static Monoid monoidDef(Definition def) { + return new Monoid<>(def); + } + + /** + * Constructs a monoid from the given definition, which must follow the monoidal + * laws. + * + * @param def The definition for the monoid. + * @return A monoid instance that uses the given sun function and zero value. + */ + public static Monoid monoidDef(AltDefinition def) { + return new Monoid<>(def); + } + + /** + * Constructs a monoid from the given semigroup definition and zero value, which must follow the monoidal laws. + * + * @param s The semigroup definition for the monoid. * @param zero The zero for the monoid. * @return A monoid instance that uses the given sun function and zero value. */ - public static Monoid monoid(final F> sum, final A zero) { - return new Monoid(sum, zero); + public static Monoid monoidDef(final Semigroup.Definition s, final A zero) { + return new Monoid<>(new Monoid.Definition() { + @Override + public A empty() { + return zero; + } + + @Override + public A sum(F0> as) { + return s.sum(zero, as); + } + + @Override + public A sum(A a, F0> as) { + return s.sum(a, as); + } + + @Override + public A multiply(int n, A a) { + return (n <= 0) + ? zero + : s.multiply1p(n - 1, a); + } + + @Override + public A multiply1p(int n, A a) { + return s.multiply1p(n, a); + } + + @Override + public A append(A a1, A a2) { + return s.append(a1, a2); + } + + @Override + public F prepend(A a) { + return s.prepend(a); + } + }); + } + + /** + * Constructs a monoid from the given semigroup definition and zero value, which must follow the monoidal laws. + * + * @param s The semigroup definition for the monoid. + * @param zero The zero for the monoid. + * @return A monoid instance that uses the given sun function and zero value. + */ + public static Monoid monoidDef(final Semigroup.AltDefinition s, final A zero) { + return monoidDef((Semigroup.Definition) s, zero); } /** * Constructs a monoid from the given sum function and zero value, which must follow the monoidal * laws. + * Java 8+ users: use {@link #monoidDef(Semigroup.Definition, Object)} instead. * * @param sum The sum function for the monoid. * @param zero The zero for the monoid. * @return A monoid instance that uses the given sun function and zero value. */ - public static Monoid monoid(final F2 sum, final A zero) { - return new Monoid(curry(sum), zero); + public static Monoid monoid(final F> sum, final A zero) { + return new Monoid<>(new AltDefinition() { + @Override + public F prepend(A a) { + return sum.f(a); + } + + @Override + public A empty() { + return zero; + } + }); } /** - * Constructs a monoid from the given semigroup and zero value, which must follow the monoidal laws. + * Constructs a monoid from the given sum function and zero value, which must follow the monoidal + * laws. * - * @param s The semigroup for the monoid. + * Java 8+ users: use {@link #monoidDef(Semigroup.Definition, Object)} instead. + * + * @param sum The sum function for the monoid. * @param zero The zero for the monoid. * @return A monoid instance that uses the given sun function and zero value. */ - public static Monoid monoid(final Semigroup s, final A zero) { - return new Monoid(s.sum(), zero); + public static Monoid monoid(final F2 sum, final A zero) { + return new Monoid<>(new Definition() { + @Override + public A empty() { + return zero; + } + + @Override + public A append(A a1, A a2) { + return sum.f(a1, a2); + } + }); } /** * A monoid that adds integers. */ - public static final Monoid intAdditionMonoid = monoid(Semigroup.intAdditionSemigroup, 0); + public static final Monoid intAdditionMonoid = monoidDef(new Definition() { + @Override + public Integer empty() { + return 0; + } + + @Override + public Integer append(Integer a1, Integer a2) { + return a1 + a2; + } + + @Override + public Integer multiply(int n, Integer i) { + return n <= 0 ? 0 : n * i; + } + }); /** * A monoid that multiplies integers. */ - public static final Monoid intMultiplicationMonoid = monoid(Semigroup.intMultiplicationSemigroup, 1); + public static final Monoid intMultiplicationMonoid = monoidDef(new Definition() { + @Override + public Integer empty() { + return 1; + } - /** - * A monoid that adds doubles. - */ - public static final Monoid doubleAdditionMonoid = monoid(Semigroup.doubleAdditionSemigroup, 0.0); + @Override + public Integer append(Integer i1, Integer i2) { + return i1 * i2; + } - /** - * A monoid that multiplies doubles. - */ - public static final Monoid doubleMultiplicationMonoid = monoid(Semigroup.doubleMultiplicationSemigroup, 1.0); + @Override + public Integer sum(F0> as) { + int x = 1; + for (Stream xs = as.f(); x != 0 && !xs.isEmpty(); xs = xs.tail()._1()) { + x *= xs.head(); + } + return x; + } + + @Override + public Integer multiply(int n, Integer integer) { + return n <= 0 ? 1 : (int) StrictMath.pow(integer.doubleValue(), n); + } + }); /** * A monoid that adds big integers. */ - public static final Monoid bigintAdditionMonoid = monoid(Semigroup.bigintAdditionSemigroup, BigInteger.ZERO); + public static final Monoid bigintAdditionMonoid = monoidDef(new Definition() { + @Override + public BigInteger empty() { + return BigInteger.ZERO; + } + + @Override + public BigInteger append(BigInteger a1, BigInteger a2) { + return a1.add(a2); + } + + @Override + public BigInteger multiply(int n, BigInteger a) { + return n <= 0 ? BigInteger.ZERO : a.multiply(BigInteger.valueOf(n)); + } + }); /** * A monoid that multiplies big integers. */ - public static final Monoid bigintMultiplicationMonoid = - monoid(Semigroup.bigintMultiplicationSemigroup, BigInteger.ONE); + public static final Monoid bigintMultiplicationMonoid = monoidDef(new Definition() { + @Override + public BigInteger empty() { + return BigInteger.ONE; + } + + @Override + public BigInteger append(BigInteger a1, BigInteger a2) { + return a1.multiply(a2); + } + + @Override + public BigInteger multiply(int n, BigInteger a) { + return n <= 0 ? BigInteger.ONE : a.pow(n); + } + + }); /** * A monoid that adds big decimals. */ public static final Monoid bigdecimalAdditionMonoid = - monoid(Semigroup.bigdecimalAdditionSemigroup, BigDecimal.ZERO); + monoidDef(new Definition() { + @Override + public BigDecimal empty() { + return BigDecimal.ZERO; + } + + @Override + public BigDecimal append(BigDecimal a1, BigDecimal a2) { + return a1.add(a2); + } + + @Override + public BigDecimal multiply(int n, BigDecimal a) { + return n <= 0 ? BigDecimal.ZERO : a.multiply(BigDecimal.valueOf(n)); + } + }); /** * A monoid that multiplies big decimals. */ public static final Monoid bigdecimalMultiplicationMonoid = - monoid(Semigroup.bigdecimalMultiplicationSemigroup, BigDecimal.ONE); + monoidDef(new Definition() { + @Override + public BigDecimal empty() { + return BigDecimal.ONE; + } + + @Override + public BigDecimal append(BigDecimal a1, BigDecimal a2) { + return a1.multiply(a2); + } + + @Override + public BigDecimal multiply(int n, BigDecimal decimal) { + return n <= 0 ? BigDecimal.ONE : decimal.pow(n); + } + }); + + /** * A monoid that adds natural numbers. */ public static final Monoid naturalAdditionMonoid = - monoid(Semigroup.naturalAdditionSemigroup, Natural.ZERO); + monoidDef(new Definition() { + @Override + public Natural empty() { + return Natural.ZERO; + } + + @Override + public Natural append(Natural a1, Natural a2) { + return a1.add(a2); + } + + @Override + public Natural multiply(int n, Natural a) { + return natural(n).map(positiveN -> a.multiply(positiveN)).orSome(Natural.ZERO); + } + }); /** * A monoid that multiplies natural numbers. */ public static final Monoid naturalMultiplicationMonoid = - monoid(Semigroup.naturalMultiplicationSemigroup, Natural.ONE); + monoidDef(new Definition() { + @Override + public Natural empty() { + return Natural.ONE; + } + + @Override + public Natural append(Natural a1, Natural a2) { + return a1.multiply(a2); + } + }); /** * A monoid that adds longs. */ - public static final Monoid longAdditionMonoid = monoid(Semigroup.longAdditionSemigroup, 0L); + public static final Monoid longAdditionMonoid = monoidDef(new Definition() { + @Override + public Long empty() { + return 0L; + } + + @Override + public Long append(Long a1, Long a2) { + return a1 + a2; + } + + @Override + public Long multiply(int n, Long a) { + return n <= 0 ? 0L : n * a; + } + }); /** * A monoid that multiplies longs. */ - public static final Monoid longMultiplicationMonoid = monoid(Semigroup.longMultiplicationSemigroup, 1L); + public static final Monoid longMultiplicationMonoid = monoidDef(new Definition() { + @Override + public Long empty() { + return 1L; + } + + @Override + public Long append(Long i1, Long i2) { + return i1 * i2; + } + + @Override + public Long sum(F0> as) { + long x = 1L; + for (Stream xs = as.f(); x != 0L && !xs.isEmpty(); xs = xs.tail()._1()) { + x *= xs.head(); + } + return x; + } + + @Override + public Long multiply(int n, Long l) { + return n <= 0 ? 1L : (long) StrictMath.pow(l.doubleValue(), n); + } + }); /** * A monoid that ORs booleans. */ - public static final Monoid disjunctionMonoid = monoid(Semigroup.disjunctionSemigroup, false); + public static final Monoid disjunctionMonoid = monoidDef(new Definition() { + @Override + public Boolean empty() { + return false; + } + + @Override + public Boolean append(Boolean a1, Boolean a2) { + return a1 | a2; + } + + @Override + public Boolean sum(F0> as) { + return as.f().filter(identity()).isNotEmpty(); + } + + @Override + public Boolean multiply(int n, Boolean a) { + return n <= 0 ? false : a; + } + }); /** * A monoid that XORs booleans. */ - public static final Monoid exclusiveDisjunctionMonoid = monoid(Semigroup.exclusiveDisjunctionSemiGroup, false); + public static final Monoid exclusiveDisjunctionMonoid = monoidDef(new Definition() { + @Override + public Boolean empty() { + return false; + } + + @Override + public Boolean append(Boolean a1, Boolean a2) { + return a1 ^ a2; + } + + @Override + public Boolean multiply(int n, Boolean a) { + return a && (n == 1); + } + }); /** * A monoid that ANDs booleans. */ - public static final Monoid conjunctionMonoid = monoid(Semigroup.conjunctionSemigroup, true); + public static final Monoid conjunctionMonoid = monoidDef(new Definition() { + @Override + public Boolean empty() { + return true; + } + + @Override + public Boolean append(Boolean a1, Boolean a2) { + return a1 & a2; + } + + @Override + public Boolean multiply(int n, Boolean a) { + return a; + } + + @Override + public Boolean sum(F0> as) { + return as.f().filter(a -> !a).isEmpty(); + } + }); /** * A monoid that appends strings. */ - public static final Monoid stringMonoid = monoid(Semigroup.stringSemigroup, ""); + public static final Monoid stringMonoid = monoidDef(new Definition() { + @Override + public String empty() { + return ""; + } + + @Override + public String append(String a1, String a2) { + return a1.concat(a2); + } + + @Override + public String sum(F0> as) { + StringBuilder sb = new StringBuilder(); + as.f().foreachDoEffect(sb::append); + return sb.toString(); + } + }); /** * A monoid that appends string buffers. */ - public static final Monoid stringBufferMonoid = monoid(Semigroup.stringBufferSemigroup, new StringBuffer()); + public static final Monoid stringBufferMonoid = monoidDef((s1, s2) -> new StringBuffer(s1).append(s2), new StringBuffer(0)); /** * A monoid that appends string builders. */ - public static final Monoid stringBuilderMonoid = monoid(Semigroup.stringBuilderSemigroup, new StringBuilder()); + public static final Monoid stringBuilderMonoid = monoidDef((s1, s2) -> new StringBuilder(s1).append(s2), new StringBuilder(0)); /** * A monoid for functions. @@ -326,7 +793,18 @@ public static Monoid monoid(final Semigroup s, final A zero) { * @return A monoid for functions. */ public static Monoid> functionMonoid(final Monoid mb) { - return monoid(Semigroup.functionSemigroup(mb.semigroup()), Function.constant(mb.zero)); + Definition mbDef = mb.def; + return monoidDef(new Definition>() { + @Override + public F empty() { + return __ -> mbDef.empty(); + } + + @Override + public F append(F a1, F a2) { + return a -> mbDef.append(a1.f(a), a2.f(a)); + } + }); } /** @@ -335,16 +813,52 @@ public static Monoid> functionMonoid(final Monoid mb) { * @return A monoid for lists. */ public static Monoid> listMonoid() { - return monoid(Semigroup.listSemigroup(), List.nil()); + return monoidDef(new Definition>() { + @Override + public List empty() { + return nil(); + } + + @Override + public List append(List a1, List a2) { + return a1.append(a2); + } + + @Override + public List sum(F0>> as) { + return as.f().map(DList::listDList).foldLeft(DList::append, DList.nil()).run(); + } + }); } /** - * A monoid for options. + * Lift a {@code Semigroup} for A to a {@code Monoid>}, using Option.none() as zero. * - * @return A monoid for options. + * @return A monoid for option. */ - public static Monoid> optionMonoid() { - return monoid(Semigroup.optionSemigroup(), Option.none()); + 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))); + } + }); } /** @@ -353,7 +867,32 @@ public static Monoid> optionMonoid() { * @return A monoid for options that take the first available value. */ public static Monoid> firstOptionMonoid() { - return monoid(Semigroup.firstOptionSemigroup(), Option.none()); + return monoidDef(new Definition>() { + @Override + public Option empty() { + return none(); + } + + @Override + public Option append(Option a1, Option a2) { + return a1.orElse(a2); + } + + @Override + public F, Option> prepend(Option a1) { + return a1.isSome() ? __ -> a1 : identity(); + } + + @Override + public Option multiply(int n, Option as) { + return as; + } + + @Override + public Option sum(F0>> as) { + return as.f().filter(Option.isSome_()).orHead(Option::none); + } + }); } /** @@ -362,7 +901,27 @@ public static Monoid> firstOptionMonoid() { * @return A monoid for options that take the last available value. */ public static Monoid> lastOptionMonoid() { - return monoid(Semigroup.lastOptionSemigroup(), Option.none()); + return monoidDef(new Definition>() { + @Override + public Option empty() { + return none(); + } + + @Override + public Option append(Option a1, Option a2) { + return a2.orElse(a1); + } + + @Override + public F, Option> prepend(Option a1) { + return a1.isNone() ? identity() : a2 -> a2.orElse(a1); + } + + @Override + public Option multiply(int n, Option as) { + return as; + } + }); } /** @@ -371,7 +930,22 @@ public static Monoid> lastOptionMonoid() { * @return A monoid for streams. */ public static Monoid> streamMonoid() { - return monoid(Semigroup.streamSemigroup(), Stream.nil()); + return monoidDef(new Definition>() { + @Override + public Stream empty() { + return Stream.nil(); + } + + @Override + public Stream append(Stream a1, Stream a2) { + return a1.append(a2); + } + + @Override + public Stream sum(F0>> as) { + return Stream.join(as.f()); + } + }); } /** @@ -379,19 +953,133 @@ public static Monoid> streamMonoid() { * * @return A monoid for arrays. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public static Monoid> arrayMonoid() { - return monoid(Semigroup.arraySemigroup(), Array.empty()); + return monoidDef(new Definition>() { + @Override + public Array empty() { + return Array.empty(); + } + + @Override + public Array append(Array a1, Array a2) { + return a1.append(a2); + } + }); + } + + /** + * A monoid for IO values. + */ + public static Monoid> ioMonoid(final Monoid ma) { + Definition maDef = ma.def; + return monoidDef(new Definition>() { + @Override + public IO empty() { + return () -> maDef.empty(); + } + + @Override + public IO append(IO a1, IO a2) { + return () -> maDef.append(a1.run(), a2.run()); + } + }); } /** - * A monoid for sets. + * A monoid for the maximum of two integers. + */ + public static final Monoid intMaxMonoid = monoidDef(new Definition() { + @Override + public Integer empty() { + return Integer.MIN_VALUE; + } + + @Override + public Integer append(Integer a1, Integer a2) { + return Math.max(a1, a2); + } + + @Override + public Integer multiply(int n, Integer a) { + return a; + } + }); + + /** + * A monoid for the minimum of two integers. + */ + public static final Monoid intMinMonoid = monoidDef(new Definition() { + @Override + public Integer empty() { + return Integer.MAX_VALUE; + } + + @Override + public Integer append(Integer a1, Integer a2) { + return Math.min(a1, a2); + } + + @Override + public Integer multiply(int n, Integer a) { + return a; + } + }); + + /** + * A monoid for the Unit value. + */ + public static final Monoid unitMonoid = monoidDef(new Definition() { + @Override + public Unit empty() { + return unit(); + } + + @Override + public Unit append(Unit a1, Unit a2) { + return unit(); + } + }); + + /** + * A union monoid for sets. * * @param o An order for set elements. * @return A monoid for sets whose elements have the given order. */ public static Monoid> setMonoid(final Ord o) { - return monoid(Semigroup.setSemigroup(), Set.empty(o)); + return monoidDef(new Definition>() { + @Override + public Set empty() { + return Set.empty(o); + } + + @Override + public Set append(Set a1, Set a2) { + return a1.union(a2); + } + }); + } + + /** + * A intersection monoid for sets. + * + * @param bounded A bound for all possible elements + * @param enumerator An enumerator for all possible elements + * @return A monoid for sets whose elements have the given order. + */ + public static Monoid> setIntersectionMonoid(final Bounded bounded, final Enumerator enumerator) { + return monoidDef(new Definition>() { + @Override + public Set empty() { + return Set.iteratorSet(enumerator.order(), enumerator.toStream(bounded).iterator()); + } + + @Override + public Set append(Set a1, Set a2) { + return a1.intersect(a2); + } + }); } } diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index a7a39cac..2bf90450 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -1,31 +1,138 @@ 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.curry; +import static fj.Function.*; +import static fj.Semigroup.semigroupDef; /** * Tests for ordering between two objects. - * - * @version %build.number% */ public final class Ord { - private final F> f; - private Ord(final F> f) { - this.f = f; + /** + * Primitives functions of Ord: minimal definition and overridable methods. + */ + public interface Definition extends Equal.Definition, Semigroup.Definition { + + F compare(A a); + + default Ordering compare(A a1, A a2) { + return compare(a1).f(a2); + } + + // equal: + @Override + default boolean equal(A a1, A a2) { + return compare(a1, a2) == Ordering.EQ; + } + + @Override + default F equal(A a) { + return compose(o -> o == Ordering.EQ, compare(a)); + } + + // max semigroup: + @Override + default A append(A a1, A a2) { + return compare(a1, a2) == Ordering.GT ? a1 : a2; + } + + @Override + default A multiply1p(int n, A a) { + return a; + } + + @Override + default F prepend(A a1) { + return apply((a2, o) -> o == Ordering.GT ? a1 : a2, compare(a1)); + } + + @Override + default Definition dual() { + return new Definition() { + @Override + public F compare(A a) { + return compose(Ordering::reverse, Definition.this.compare(a)); + } + + @Override + public Ordering compare(A a1, A a2) { + return Definition.this.compare(a2, a1); + } + + @Override + public Definition dual() { + return Definition.this; + } + }; + } + + /** + * 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); + } + } + + /** + * Primitives functions of Ord: alternative minimal definition and overridable methods. + */ + public interface AltDefinition extends Definition { + + Ordering compare(A a1, A a2); + + default F compare(A a1) { + return a2 -> compare(a1, a2); + } + + @Override + default F prepend(A a1) { + return a2 -> append(a1, a2); + } + + } + + + private final Definition def; + + private Ord(final Definition def) { + this.def = def; + this.max = a1 -> apply((a2, o) -> o == Ordering.GT ? a1 : a2, def.compare(a1)); + this.min = a1 -> apply((a2, o) -> o == Ordering.LT ? a1 : a2, def.compare(a1)); } /** @@ -34,7 +141,7 @@ private Ord(final F> f) { * @return A function that returns an ordering for its arguments. */ public F> compare() { - return f; + return def::compare; } /** @@ -45,7 +152,7 @@ public F> compare() { * @return An ordering for the given arguments. */ public Ordering compare(final A a1, final A a2) { - return f.f(a1).f(a2); + return def.compare(a1, a2); } /** @@ -56,7 +163,7 @@ public Ordering compare(final A a1, final A a2) { * @return true if the given arguments are equal, false otherwise. */ public boolean eq(final A a1, final A a2) { - return compare(a1, a2) == Ordering.EQ; + return def.compare(a1, a2) == Ordering.EQ; } /** @@ -65,7 +172,7 @@ public boolean eq(final A a1, final A a2) { * @return An Equal for this order. */ public Equal equal() { - return Equal.equal(curry(this::eq)); + return Equal.equalDef(def); } /** @@ -75,7 +182,7 @@ public Equal equal() { * @return A new ord. */ public Ord contramap(final F f) { - return ord(F1Functions.o(F1Functions.o(F1Functions.andThen(f), this.f), f)); + return ordDef(contramapDef(f, def)); } /** @@ -88,9 +195,22 @@ public Ord contramap(final F f) { * false otherwise. */ public boolean isLessThan(final A a1, final A a2) { - return compare(a1, a2) == Ordering.LT; + return def.compare(a1, a2) == Ordering.LT; } + /** + * Returns true if the first given argument is less than or equal to the second given argument, + * false otherwise. + * + * @param a1 An instance to compare for ordering to another. + * @param a2 An instance to compare for ordering to another. + * @return true if the first given argument is less than or equal to the second given argument, + * false otherwise. + */ + public boolean isLessThanOrEqualTo(final A a1, final A a2) { + return def.compare(a1, a2) != Ordering.GT; + } + /** * Returns true if the first given argument is greater than the second given * argument, false otherwise. @@ -101,7 +221,7 @@ public boolean isLessThan(final A a1, final A a2) { * argument, false otherwise. */ public boolean isGreaterThan(final A a1, final A a2) { - return compare(a1, a2) == Ordering.GT; + return def.compare(a1, a2) == Ordering.GT; } /** @@ -111,7 +231,7 @@ public boolean isGreaterThan(final A a1, final A a2) { * @return A function that returns true if its argument is less than the argument to this method. */ public F isLessThan(final A a) { - return a2 -> compare(a2, a) == Ordering.LT; + return compose(o -> o == Ordering.GT, def.compare(a)); } /** @@ -121,7 +241,7 @@ public F isLessThan(final A a) { * @return A function that returns true if its argument is greater than the argument to this method. */ public F isGreaterThan(final A a) { - return a2 -> compare(a2, a) == Ordering.GT; + return compose(o -> o == Ordering.LT, def.compare(a)); } /** @@ -132,7 +252,7 @@ public F isGreaterThan(final A a) { * @return The greater of the two values. */ public A max(final A a1, final A a2) { - return isGreaterThan(a1, a2) ? a1 : a2; + return def.append(a1, a2); } @@ -149,120 +269,163 @@ public A min(final A a1, final A a2) { /** * A function that returns the greater of its two arguments. + * */ - public final F> max = curry((a, a1) -> max(a, a1)); + public final F> max; /** * A function that returns the lesser of its two arguments. */ - public final F> min = curry((a, a1) -> min(a, a1)); + public final F> min; + + public final Semigroup minSemigroup() { + return semigroupDef(def.dual()); + } - public final Ord reverse() { return ord(Function.flip(f)); } + public final Monoid minMonoid(A zero) { + return Monoid.monoidDef(def.dual(), zero); + } + + public final Semigroup maxSemigroup() { + return semigroupDef(def); + } + + public final Monoid maxMonoid(A zero) { + return Monoid.monoidDef(def, zero); + } + + public final Ord reverse() { + return ordDef(def.dual()); + } + + /** + * Begin definition of an ord instance. + * @see Definition#then(F, Equal) + */ + 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 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)); + } + }; + } /** * Returns an order instance that uses the given equality test and ordering function. * + * Java 8+ users: use {@link #ordDef(Definition)} instead. + * * @param f The order function. * @return An order instance. */ public static Ord ord(final F> f) { - return new Ord(f); + return new Ord<>(f::f); + } + + /** + * Returns an order instance that uses the given equality test and ordering function. + * + * Java 8+ users: use {@link #ordDef(AltDefinition)} instead. + * + * @param f The order function. + * @return An order instance. + */ + public static Ord ord(final F2 f) { + return ordDef(f::f); + } + + /** + * Returns an order instance that uses the given minimal equality test and ordering definition. + * + * @param def The order definition. + * @return An order instance. + */ + public static Ord ordDef(final Definition def) { + return new Ord<>(def); + } + + /** + * Returns an order instance that uses the given minimal equality test and ordering definition. + * + * @param def The order definition. + * @return An order instance. + */ + public static Ord ordDef(final AltDefinition def) { + return new Ord<>(def); } + /** * An order instance for the boolean type. */ - public static final Ord booleanOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord booleanOrd = comparableOrd(); /** * An order instance for the byte type. */ - public static final Ord byteOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord byteOrd = comparableOrd(); /** * An order instance for the char type. */ - public static final Ord charOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord charOrd = comparableOrd(); /** * An order instance for the double type. */ - public static final Ord doubleOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord doubleOrd = comparableOrd(); /** * An order instance for the float type. */ - public static final Ord floatOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord floatOrd = comparableOrd(); /** * An order instance for the int type. */ - public static final Ord intOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord intOrd = comparableOrd(); /** * An order instance for the BigInteger type. */ - public static final Ord bigintOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord bigintOrd = comparableOrd(); /** * An order instance for the BigDecimal type. */ - public static final Ord bigdecimalOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord bigdecimalOrd = comparableOrd(); /** * An order instance for the long type. */ - public static final Ord longOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord longOrd = comparableOrd(); /** * An order instance for the short type. */ - public static final Ord shortOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord shortOrd = comparableOrd(); /** * An order instance for the {@link Ordering} type. */ - public static final Ord orderingOrd = Ord.ord(curry((o1, o2) -> o1 == o2 ? + public static final Ord orderingOrd = ordDef((o1, o2) -> o1 == o2 ? Ordering.EQ : o1 == Ordering.LT ? Ordering.LT : @@ -270,28 +433,22 @@ public static Ord ord(final F> f) { Ordering.GT : o1 == Ordering.EQ ? Ordering.LT : - Ordering.GT)); + Ordering.GT); /** * An order instance for the {@link String} type. */ - public static final Ord stringOrd = ord( - a1 -> a2 -> { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); + public static final Ord stringOrd = comparableOrd(); /** * An order instance for the {@link StringBuffer} type. */ - public static final Ord stringBufferOrd = - ord(a1 -> a2 -> stringOrd.compare(a1.toString(), a2.toString())); + public static final Ord stringBufferOrd = stringOrd.contramap(StringBuffer::toString); /** * An order instance for the {@link StringBuffer} type. */ - public static final Ord stringBuilderOrd = - ord(a1 -> a2 -> stringOrd.compare(a1.toString(), a2.toString())); + public static final Ord stringBuilderOrd = stringOrd.contramap(StringBuilder::toString); /** * An order instance for the {@link Option} type. @@ -300,13 +457,14 @@ public static Ord ord(final F> f) { * @return An order instance for the {@link Option} type. */ public static Ord> optionOrd(final Ord oa) { - return ord(o1 -> o2 -> o1.isNone() ? + Definition oaDef = oa.def; + return ordDef((o1, o2) -> o1.isNone() ? o2.isNone() ? Ordering.EQ : Ordering.LT : o2.isNone() ? Ordering.GT : - oa.f.f(o1.some()).f(o2.some())); + oaDef.compare(o1.some()).f(o2.some())); } /** @@ -317,13 +475,15 @@ public static Ord> optionOrd(final Ord oa) { * @return An order instance for the {@link Either} type. */ public static Ord> eitherOrd(final Ord oa, final Ord ob) { - return ord(e1 -> e2 -> e1.isLeft() ? + Definition oaDef = oa.def; + Definition obDef = ob.def; + return ordDef((e1, e2) -> e1.isLeft() ? e2.isLeft() ? - oa.f.f(e1.left().value()).f(e2.left().value()) : - Ordering.LT : + oaDef.compare(e1.left().value()).f(e2.left().value()) : + Ordering.LT : e2.isLeft() ? - Ordering.GT : - ob.f.f(e1.right().value()).f(e2.right().value())); + Ordering.GT : + obDef.compare(e1.right().value()).f(e2.right().value())); } /** @@ -334,7 +494,7 @@ public static Ord> eitherOrd(final Ord oa, final Ord o * @return An order instance for the {@link Validation} type. */ public static Ord> validationOrd(final Ord oa, final Ord ob) { - return eitherOrd(oa, ob).contramap(Validation.either()); + return eitherOrd(oa, ob).contramap(Validation.either()); } /** @@ -344,7 +504,7 @@ public static Ord> validationOrd(final Ord oa, final * @return An order instance for the {@link List} type. */ public static Ord> listOrd(final Ord oa) { - return ord(l1 -> l2 -> { + return ordDef((l1, l2) -> { List x1 = l1; List x2 = l2; @@ -367,6 +527,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. * @@ -374,7 +565,7 @@ public static Ord> listOrd(final Ord oa) { * @return An order instance for the {@link NonEmptyList} type. */ public static Ord> nonEmptyListOrd(final Ord oa) { - return listOrd(oa).contramap(NonEmptyList.toList_()); + return listOrd(oa).contramap(NonEmptyList.toList_()); } /** @@ -384,14 +575,15 @@ public static Ord> nonEmptyListOrd(final Ord oa) { * @return An order instance for the {@link Stream} type. */ public static Ord> streamOrd(final Ord oa) { - return ord(s1 -> s2 -> { + return ordDef((s1, s2) -> { if (s1.isEmpty()) return s2.isEmpty() ? Ordering.EQ : Ordering.LT; else if (s2.isEmpty()) return s1.isEmpty() ? Ordering.EQ : Ordering.GT; else { final Ordering c = oa.compare(s1.head(), s2.head()); - return c == Ordering.EQ ? streamOrd(oa).f.f(s1.tail()._1()).f(s2.tail()._1()) : c; + // FIXME: not stack safe + return c == Ordering.EQ ? streamOrd(oa).def.compare(s1.tail()._1()).f(s2.tail()._1()) : c; } }); } @@ -403,7 +595,7 @@ else if (s2.isEmpty()) * @return An order instance for the {@link Array} type. */ public static Ord> arrayOrd(final Ord oa) { - return ord(a1 -> a2 -> { + return ordDef((a1, a2) -> { int i = 0; //noinspection ForLoopWithMissingComponent for (; i < a1.length() && i < a2.length(); i++) { @@ -428,13 +620,13 @@ public static Ord> arrayOrd(final Ord oa) { * @return An order instance for the {@link Set} type. */ public static Ord> setOrd(final Ord oa) { - return streamOrd(oa).contramap(as -> as.toStream()); + return streamOrd(oa).contramap(Set::toStream); } /** * An order instance for the {@link Unit} type. */ - public static final Ord unitOrd = ord(curry((Unit u1, Unit u2) -> Ordering.EQ)); + public static final Ord unitOrd = ordDef((u1, u2) -> Ordering.EQ); /** * An order instance for a product-1. @@ -443,7 +635,7 @@ public static Ord> setOrd(final Ord oa) { * @return An order instance for a product-1. */ public static Ord> p1Ord(final Ord oa) { - return oa.contramap(P1.__1()); + return oa.contramap(P1.__1()); } @@ -455,15 +647,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 ord(curry((P2 a, P2 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 ord(p1 -> p2 -> oa.compare(p1._1(), p2._1())); + return on(P2.__1(), oa).ord(); } public static Ord> p2Ord2(Ord ob) { - return ord(p1 -> p2 -> ob.compare(p1._2(), p2._2())); + return on(P2.__2(), ob).ord(); } /** @@ -475,9 +667,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 ord(curry((P3 a, P3 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(); } /** @@ -492,42 +682,12 @@ public static Ord> p3Ord(final Ord oa, final Ord ob, * @return An order instance for the Comparable interface. */ public static > Ord comparableOrd() { - - return ord(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 (check {@link #hashEqualsOrd()} - * for an additional check on {@link Object#equals(Object)}). - * - * @return An order instance that is based on {@link Object#hashCode()}. - * @see #hashEqualsOrd() - */ - public static Ord hashOrd() { - return ord(a -> a2 -> { - final int x = a.hashCode() - a2.hashCode(); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - }); - } - - /** - * 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}. - * - * @return An order instance that is based on {@link Object#hashCode()} and {@link Object#equals}. - */ - public static Ord hashEqualsOrd() { - return ord(a -> a2 -> { - final int x = a.hashCode() - a2.hashCode(); - return x < 0 ? Ordering.LT : x == 0 && a.equals(a2) ? Ordering.EQ : Ordering.GT; - }); + return ordDef((a1, a2) -> Ordering.fromInt(a1.compareTo(a2))); } class OrdComparator implements Comparator { @Override - public int compare(A o1, A o2) { + public final int compare(A o1, A o2) { return Ord.this.compare(o1, o2).toInt(); } } 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 073de4f6..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() { @@ -18,7 +16,7 @@ private P() { * @return A function that puts an element in a product-1. */ public static F> p1() { - return a -> p(a); + return P::p; } /** @@ -32,17 +30,59 @@ public static P1 p(final A a) { @Override public A _1() { return a; } - @Override public P1 memo() { return this; } + @Override public P1 hardMemo() { return this; } @Override public P1 weakMemo() { return this; } @Override public P1 softMemo() { return this; } }; } + /** + * Convert a F0 into a P1, using call-by-need semantic: + * function f is evaluated at most once, at first to {@link P1#_1()}. + */ + public static P1 hardMemo(F0 f) { + return new P1.Memo<>(f); + } + + /** + * Convert a F0 into a P1, using weak call-by-need semantic: + * function f is evaluated at first call to {@link P1#_1()} + * and at each subsequent call if and only if the reference have been garbage collected. + */ + public static P1 weakMemo(F0 f) { + return new P1.WeakReferenceMemo<>(f); + } - public static P1 lazy(final P1 pa) { - return pa; + /** + * Convert a F0 into a P1, using soft call-by-need semantic: + * function f is evaluated at first call to {@link P1#_1()} + * and at each subsequent call if and only if the reference have been garbage collected + * due of shortage of memory (ie. to avoid OutOfMemoryErrors). + */ + 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()}. + */ + public static P1 lazy(F0 f) { + return new P1() { + @Override + public A _1() { + return f.f(); + } + }; + } + public static P2 lazy(final F0 pa, final F0 pb) { return new P2() { @Override @@ -234,7 +274,7 @@ public H _8() { } public static P2 lazyProduct(F0> f) { - return P.lazy(() -> f.f()._1(), () -> f.f()._2()); + return lazy(() -> f.f()._1(), () -> f.f()._2()); } /** @@ -538,21 +578,12 @@ public H _8() { }; } - public static P1 lazy(F0 f) { - return new P1() { - @Override - public A _1() { - return f.f(); - } - }; - } - public static P1 lazy(F f) { return lazy(() -> f.f(unit())); } public static P2 lazy(F fa, F fb) { - return P.lazy(() -> fa.f(unit()), () -> fb.f(unit())); + return lazy(() -> fa.f(unit()), () -> fb.f(unit())); } public static P3 lazy(F fa, F fb, F fc) { diff --git a/core/src/main/java/fj/P1.java b/core/src/main/java/fj/P1.java index 1995352e..5e575a4d 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -1,17 +1,12 @@ package fj; +import fj.data.*; + import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; -import fj.data.Array; -import fj.data.List; -import fj.data.Stream; -import fj.data.Either; -import fj.data.Option; -import fj.data.Validation; -//import fj.data.*; - +import static fj.P.p; public abstract class P1 implements F0 { @@ -33,18 +28,7 @@ public final A f() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return p -> p._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. - */ - public static F, P1> fmap(final F f) { - return map_(f); + return P1::_1; } /** @@ -63,7 +47,7 @@ public static F, P1> map_(final F f) { * @param f A function to apply to the value in a product-1. * @return The result of applying the given function to the value of given product-1. */ - public P1 bind(final F> f) { + public final P1 bind(final F> f) { P1 self = this; return P.lazy(() -> f.f(self._1())._1()); } @@ -84,9 +68,9 @@ public static F> curry(final F f) { * @param cf The P1 function to apply. * @return A new P1 after applying the given P1 function to the first argument. */ - public P1 apply(final P1> cf) { + public final P1 apply(final P1> cf) { P1 self = this; - return cf.bind(f -> fmap(f).f(self)); + return cf.bind(f -> map_(f).f(self)); } /** @@ -96,15 +80,15 @@ public P1 apply(final P1> cf) { * @param f The function to apply to the values in the given P1s. * @return A new P1 after performing the map, then final join. */ - public P1 bind(final P1 cb, final F> f) { - return cb.apply(fmap(f).f(this)); + public final P1 bind(final P1 cb, final F> f) { + return cb.apply(map_(f).f(this)); } /** * Binds the given function to the values in the given P1s with a final join. */ - public P1 bind(final P1 cb, final F2 f) { - return bind(cb, F2W.lift(f).curry()); + public final P1 bind(final P1 cb, final F2 f) { + return bind(cb, f.curry()); } /** @@ -114,7 +98,7 @@ public P1 bind(final P1 cb, final F2 f) { * @return A new P1 that is the join of the given P1. */ public static P1 join(final P1> a) { - return a.bind(Function.>identity()); + return a.bind(Function.identity()); } /** @@ -127,7 +111,7 @@ public static F, F, P1>> liftM2(final F> f) return Function.curry((pa, pb) -> pa.bind(pb, f)); } - public P1 liftM2(P1 pb, F2 f) { + public final P1 liftM2(P1 pb, F2 f) { return P.lazy(() -> f.f(_1(), pb._1())); } @@ -138,7 +122,7 @@ public P1 liftM2(P1 pb, F2 f) { * @return A single P1 for the given List. */ public static P1> sequence(final List> as) { - return as.foldRight(liftM2(List.cons()), P.p(List.nil())); + return as.foldRight(liftM2(List.cons()), p(List.nil())); } /** @@ -147,7 +131,7 @@ public static P1> sequence(final List> as) { * @return A function from a List of P1s to a single P1 of a List. */ public static F>, P1>> sequenceList() { - return as -> sequence(as); + return P1::sequence; } /** @@ -157,14 +141,14 @@ public static F>, P1>> sequenceList() { * @return A single P1 for the given stream. */ public static P1> sequence(final Stream> as) { - return as.foldRight(liftM2(Stream.cons()), P.p(Stream.nil())); + return as.foldRight(liftM2(Stream.cons()), p(Stream.nil())); } /** * Turns an optional P1 into a lazy option. */ public static P1> sequence(final Option> o) { - return P.lazy(() -> o.map(p -> p._1())); + return P.lazy(() -> o.map(P1::_1)); } /** @@ -174,7 +158,7 @@ public static P1> sequence(final Option> o) { * @return A single P1 for the given array. */ public static P1> sequence(final Array> as) { - return P.lazy(() -> as.map(P1.__1())); + return P.lazy(() -> as.map(P1.__1())); } /** @@ -183,8 +167,8 @@ public static P1> sequence(final Array> as) { * @param f The function that takes A and produces a List (non-deterministic result) * @return A List of P1 */ - public List> traverseList(final F> f){ - return f.f(_1()).map(b -> P.p(b)); + public final List> traverseList(final F> f){ + return f.f(_1()).map(P::p); } /** @@ -193,8 +177,8 @@ public List> traverseList(final F> f){ * @param f The function produces Either * @return An Either of P1 */ - public Either> traverseEither(final F> f){ - return f.f(_1()).right().map(b -> P.p(b)); + public final Either> traverseEither(final F> f){ + return f.f(_1()).right().map(P::p); } /** @@ -203,8 +187,8 @@ public Either> traverseEither(final F> f){ * @param f The function that produces Option * @return An Option of P1 */ - public Option> traverseOption(final F> f){ - return f.f(_1()).map(b -> P.p(b)); + public final Option> traverseOption(final F> f){ + return f.f(_1()).map(P::p); } /** @@ -213,8 +197,8 @@ public Option> traverseOption(final F> f){ * @param f The function might produces Validation * @return An Validation of P1 */ - public Validation> traverseValidation(final F> f){ - return f.f(_1()).map(b -> P.p(b)); + public final Validation> traverseValidation(final F> f){ + return f.f(_1()).map(P::p); } /** @@ -223,8 +207,8 @@ public Validation> traverseValidation(final F */ - public Stream> traverseStream(final F> f){ - return f.f(_1()).map(b -> P.p(b)); + public final Stream> traverseStream(final F> f){ + return f.f(_1()).map(P::p); } /** @@ -233,12 +217,15 @@ public Stream> traverseStream(final F> f){ * @param f The function to map with. * @return A product with the given function applied. */ - public P1 map(final F f) { + public final P1 map(final F f) { final P1 self = this; return P.lazy(() -> f.f(self._1())); } - public P1 memo() { + /** + * Wrap the memoized value into a WeakReference. + */ + public final P1 memo() { return weakMemo(); } @@ -259,72 +246,69 @@ public P1 memo() { */ public P1 softMemo() { return new SoftReferenceMemo<>(this); } - public static P1 memo(F f) { - return P.lazy(f).memo(); - } + static final class Memo extends P1 { + private volatile F0 fa; + private A value; - public static P1 memo(F0 f) { - return P.lazy(f).memo(); - } + Memo(F0 fa) { this.fa = fa; } - static class Memo extends P1 { - private final P1 self; - private volatile boolean initialized; - private A value; + @Override public final A _1() { + return (fa == null) ? value : computeValue(); + } - Memo(P1 self) { this.self = self; } - - @Override public A _1() { - if (!initialized) { - synchronized (this) { - if (!initialized) { - A a = self._1(); - value = a; - initialized = true; - return a; - } - } + private synchronized A computeValue() { + F0 fa = this.fa; + if (fa != null) { + value = fa.f(); + this.fa = null; } return value; } - @Override public P1 memo() { return this; } + @Override public P1 hardMemo() { return this; } + @Override public P1 softMemo() { return this; } + @Override public P1 weakMemo() { return this; } } abstract static class ReferenceMemo extends P1 { - private final P1 self; - private final Object latch = new Object(); - private volatile Reference> v = null; - - ReferenceMemo(final P1 self) { this.self = self; } - - @Override public A _1() { - Option o = v != null ? v.get() : null; - if (o == null) { - synchronized (latch) { - o = v != null ? v.get() : null; - if (o == null) { - o = Option.some(self._1()); - v = newReference(o); - } - } + private final F0 fa; + private volatile Reference> v = null; + + ReferenceMemo(final F0 fa) { this.fa = fa; } + + @Override public final A _1() { + Reference> v = this.v; + P1 p1 = v != null ? v.get() : null; + return p1 != null ? p1._1() : computeValue(); + } + + private synchronized A computeValue() { + Reference> v = this.v; + P1 p1 = v != null ? v.get() : null; + if (p1 == null) { + A a = fa.f(); + this.v = newReference(p(a)); + return a; } - return o.some(); + return p1._1(); } - abstract Reference> newReference(Option o); + abstract Reference newReference(B ref); } - static class WeakReferenceMemo extends ReferenceMemo { - WeakReferenceMemo(P1 self) { super(self); } - @Override Reference> newReference(final Option o) { return new WeakReference<>(o); } + static final class WeakReferenceMemo extends ReferenceMemo { + WeakReferenceMemo(F0 fa) { super(fa); } + @Override + Reference newReference(final B ref) { return new WeakReference<>(ref); } @Override public P1 weakMemo() { return this; } } - static class SoftReferenceMemo extends ReferenceMemo { - SoftReferenceMemo(P1 self) { super(self); } - @Override Reference> newReference(final Option o) { return new SoftReference<>(o); } + static final class SoftReferenceMemo extends ReferenceMemo { + SoftReferenceMemo(F0 self) { super(self); } + @Override + Reference newReference(final B ref) { return new SoftReference<>(ref); } @Override public P1 softMemo() { return this; } + @Override public P1 weakMemo() { return this; } } /** @@ -332,20 +316,20 @@ static class SoftReferenceMemo extends ReferenceMemo { * * @return A constant function that always uses this value. */ - public F constant() { return Function.constant(_1()); } + public final F constant() { return Function.constant(_1()); } @Override - public String toString() { + public final String toString() { return Show.p1Show(Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { - return Equal.equals0(P1.class, this, other, () -> Equal.p1Equal(Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(P1.class, this, other, () -> Equal.p1Equal(Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p1Hash(Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P2.java b/core/src/main/java/fj/P2.java index 3283f344..b4979f03 100644 --- a/core/src/main/java/fj/P2.java +++ b/core/src/main/java/fj/P2.java @@ -1,6 +1,7 @@ package fj; import static fj.Function.*; +import static fj.P.weakMemo; import static fj.data.optic.PLens.pLens; import fj.data.*; import fj.data.optic.Lens; @@ -8,8 +9,6 @@ /** * A product-2. - * - * @version %build.number% */ public abstract class P2 { /** @@ -27,12 +26,12 @@ public abstract class P2 { public abstract B _2(); @Override - public boolean equals(Object other) { - return Equal.equals0(P2.class, this, other, () -> Equal.p2Equal(Equal.anyEqual(), Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(P2.class, this, other, () -> Equal.p2Equal(Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p2Hash(Hash.anyHash(), Hash.anyHash()).hash(this); } @@ -42,7 +41,7 @@ public int hashCode() { * @return A new product-2 with the elements swapped. */ public final P2 swap() { - return P.lazy(() -> P2.this._2(), () -> P2.this._1()); + return P.lazy(P2.this::_2, P2.this::_1); } /** @@ -53,7 +52,7 @@ public final P2 swap() { */ public final P2 map1(final F f) { P2 self = this; - return P.lazy(() -> f.f(self._1()), () -> self._2()); + return P.lazy(() -> f.f(self._1()), self::_2); } /** @@ -63,7 +62,7 @@ public final P2 map1(final F f) { * @return A product with the given function applied. */ public final P2 map2(final F f) { - return P.lazy(() -> P2.this._1(), () -> f.f(P2.this._2())); + return P.lazy(P2.this::_1, () -> f.f(P2.this._2())); } @@ -90,7 +89,7 @@ public final P2 split(final F f, final F g) { */ public final P2 cobind(final F, C> k) { P2 self = this; - return P.lazy(() -> k.f(self), () -> self._2()); + return P.lazy(() -> k.f(self), self::_2); } /** @@ -155,7 +154,7 @@ public final Either> traverseEither(final F> */ public final Stream sequenceW(final Stream, C>> fs) { return fs.isEmpty() - ? Stream.nil() + ? Stream.nil() : Stream.cons(fs.head().f(this), () -> sequenceW(fs.tail()._1())); } @@ -165,7 +164,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); } /** @@ -174,10 +173,71 @@ 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); } - /** + /** + * 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. @@ -185,8 +245,8 @@ public final P1 _2_() { public final P2 memo() { P2 self = this; return new P2() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); public A _1() { return a._1(); @@ -258,7 +318,7 @@ public static P2 map(final F f, final P2 p) { * @return A curried form of {@link #swap()}. */ public static F, P2> swap_() { - return p -> p.swap(); + return P2::swap; } /** @@ -267,7 +327,7 @@ public static F, P2> swap_() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return p -> p._1(); + return P2::_1; } /** @@ -276,7 +336,7 @@ public static F, A> __1() { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return p -> p._2(); + return P2::_2; } /** @@ -311,7 +371,7 @@ public static F2 untuple(final F, C> f) { @Override - public String toString() { + public final String toString() { return Show.p2Show(Show.anyShow(), Show.anyShow()).showS(this); } @@ -335,7 +395,7 @@ public static PLens, P2, A, C> _1p() { /** * Monomorphic lens targeted on _1. */ - public static Lens, A> _1() { + public static Lens, A> _1() { return new Lens<>(_1p()); } @@ -349,7 +409,7 @@ public static PLens, P2, B, C> _2p() { /** * Monomorphic lens targeted on _1. */ - public static Lens, B> _2() { + public static Lens, B> _2() { return new Lens<>(_2p()); } diff --git a/core/src/main/java/fj/P3.java b/core/src/main/java/fj/P3.java index 9f473f56..fff39ed8 100644 --- a/core/src/main/java/fj/P3.java +++ b/core/src/main/java/fj/P3.java @@ -1,9 +1,9 @@ package fj; +import static fj.P.weakMemo; + /** * A product-3. - * - * @version %build.number% */ public abstract class P3 { /** @@ -99,7 +99,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); } /** @@ -108,7 +108,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); } /** @@ -117,9 +117,61 @@ 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); + } + + + /** + * 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. * @@ -128,9 +180,9 @@ public final P1 _3_() { public final P3 memo() { P3 self = this; return new P3() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); public A _1() { return a._1(); @@ -152,7 +204,7 @@ public C _3() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return p -> p._1(); + return P3::_1; } /** @@ -161,7 +213,7 @@ public static F, A> __1() { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return p -> p._2(); + return P3::_2; } /** @@ -170,22 +222,22 @@ public static F, B> __2() { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return p -> p._3(); + return P3::_3; } @Override - public String toString() { + public final String toString() { return Show.p3Show(Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P3.class, this, other, - () -> Equal.p3Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p3Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p3Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P4.java b/core/src/main/java/fj/P4.java index 7ba55b5d..d2dca64b 100644 --- a/core/src/main/java/fj/P4.java +++ b/core/src/main/java/fj/P4.java @@ -1,9 +1,9 @@ package fj; +import static fj.P.weakMemo; + /** * A product-4. - * - * @version %build.number% */ public abstract class P4 { /** @@ -144,7 +144,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); } /** @@ -153,7 +153,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); } /** @@ -162,7 +162,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); } /** @@ -171,9 +171,53 @@ 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); + } + + + /** + * 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. * @@ -182,10 +226,10 @@ public final P1 _4_() { public final P4 memo() { P4 self = this; return new P4() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); - private final P1 d = P1.memo(u -> self._4()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); + private final P1 d = weakMemo(self::_4); public A _1() { return a._1(); @@ -212,7 +256,7 @@ public D _4() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return p -> p._1(); + return P4::_1; } /** @@ -221,7 +265,7 @@ public static F, A> __1() { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return p -> p._2(); + return P4::_2; } /** @@ -230,7 +274,7 @@ public static F, B> __2() { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return p -> p._3(); + return P4::_3; } /** @@ -239,22 +283,22 @@ public static F, C> __3() { * @return A function that returns the fourth element of a product. */ public static F, D> __4() { - return p -> p._4(); + return P4::_4; } @Override - public String toString() { + public final String toString() { return Show.p4Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P4.class, this, other, - () -> Equal.p4Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p4Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p4Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P5.java b/core/src/main/java/fj/P5.java index 6059e22a..07138cb3 100644 --- a/core/src/main/java/fj/P5.java +++ b/core/src/main/java/fj/P5.java @@ -1,9 +1,9 @@ package fj; +import static fj.P.weakMemo; + /** * A product-5. - * - * @version %build.number% */ public abstract class P5 { /** @@ -197,7 +197,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); } /** @@ -206,7 +206,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); } /** @@ -215,7 +215,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); } /** @@ -224,7 +224,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); } /** @@ -233,7 +233,37 @@ 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); + } + + /** + * 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()); } /** @@ -244,11 +274,11 @@ public final P1 _5_() { public final P5 memo() { P5 self = this; return new P5() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); - private final P1 d = P1.memo(u -> self._4()); - private final P1 e = P1.memo(u -> self._5()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); + private final P1 d = weakMemo(self::_4); + private final P1 e = weakMemo(self::_5); public A _1() { return a._1(); @@ -278,7 +308,7 @@ public E _5() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return p -> p._1(); + return P5::_1; } /** @@ -287,7 +317,7 @@ public static F, A> __1() { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return p -> p._2(); + return P5::_2; } /** @@ -296,7 +326,7 @@ public static F, B> __2() { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return p -> p._3(); + return P5::_3; } /** @@ -305,7 +335,7 @@ public static F, C> __3() { * @return A function that returns the fourth element of a product. */ public static F, D> __4() { - return p -> p._4(); + return P5::_4; } /** @@ -314,22 +344,22 @@ public static F, D> __4() { * @return A function that returns the fifth element of a product. */ public static F, E> __5() { - return p -> p._5(); + return P5::_5; } @Override - public String toString() { + public final String toString() { return Show.p5Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P5.class, this, other, - () -> Equal.p5Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p5Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p5Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P6.java b/core/src/main/java/fj/P6.java index a6ed6c8f..295b5427 100644 --- a/core/src/main/java/fj/P6.java +++ b/core/src/main/java/fj/P6.java @@ -1,11 +1,11 @@ package fj; +import static fj.P.weakMemo; + /** * A product-6. - * - * @version %build.number% */ -@SuppressWarnings({"UnnecessaryFullyQualifiedName"}) +@SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P6 { /** * Access the first element of the product. @@ -259,7 +259,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); } /** @@ -268,7 +268,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); } /** @@ -277,7 +277,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); } /** @@ -286,7 +286,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); } /** @@ -295,7 +295,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); } /** @@ -304,7 +304,27 @@ 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); + } + + /** + * 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()); } /** @@ -315,12 +335,12 @@ public final P1 _6_() { public final P6 memo() { P6 self = this; return new P6() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); - private final P1 d = P1.memo(u -> self._4()); - private final P1 e = P1.memo(u -> self._5()); - private final P1 f = P1.memo(u -> self._6()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); + private final P1 d = weakMemo(self::_4); + private final P1 e = weakMemo(self::_5); + private final P1 f = weakMemo(self::_6); public A _1() { return a._1(); @@ -355,11 +375,7 @@ public F _6() { * @return A function that returns the first element of a product. */ public static fj.F, A> __1() { - return new fj.F, A>() { - public A f(final P6 p) { - return p._1(); - } - }; + return P6::_1; } /** @@ -368,11 +384,7 @@ public A f(final P6 p) { * @return A function that returns the second element of a product. */ public static fj.F, B> __2() { - return new fj.F, B>() { - public B f(final P6 p) { - return p._2(); - } - }; + return P6::_2; } /** @@ -381,11 +393,7 @@ public B f(final P6 p) { * @return A function that returns the third element of a product. */ public static fj.F, C> __3() { - return new fj.F, C>() { - public C f(final P6 p) { - return p._3(); - } - }; + return P6::_3; } /** @@ -394,11 +402,7 @@ public C f(final P6 p) { * @return A function that returns the fourth element of a product. */ public static fj.F, D> __4() { - return new fj.F, D>() { - public D f(final P6 p) { - return p._4(); - } - }; + return P6::_4; } /** @@ -407,11 +411,7 @@ public D f(final P6 p) { * @return A function that returns the fifth element of a product. */ public static fj.F, E> __5() { - return new fj.F, E>() { - public E f(final P6 p) { - return p._5(); - } - }; + return P6::_5; } /** @@ -420,27 +420,23 @@ public E f(final P6 p) { * @return A function that returns the sixth element of a product. */ public static fj.F, F$> __6() { - return new fj.F, F$>() { - public F$ f(final P6 p) { - return p._6(); - } - }; + return P6::_6; } @Override - public String toString() { + public final String toString() { return Show.p6Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P6.class, this, other, - () -> Equal.p6Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p6Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p6Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P7.java b/core/src/main/java/fj/P7.java index 76900fc4..17161f5d 100644 --- a/core/src/main/java/fj/P7.java +++ b/core/src/main/java/fj/P7.java @@ -1,11 +1,11 @@ package fj; +import static fj.P.weakMemo; + /** * A product-7. - * - * @version %build.number% */ -@SuppressWarnings({"UnnecessaryFullyQualifiedName"}) +@SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P7 { /** * Access the first element of the product. @@ -328,7 +328,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); } /** @@ -337,7 +337,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); } /** @@ -346,7 +346,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); } /** @@ -355,7 +355,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); } /** @@ -364,7 +364,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); } /** @@ -373,7 +373,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); } /** @@ -382,7 +382,17 @@ 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); + } + + /** + * 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); } /** @@ -393,13 +403,13 @@ public final P1 _7_() { public final P7 memo() { P7 self = this; return new P7() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); - private final P1 d = P1.memo(u -> self._4()); - private final P1 e = P1.memo(u -> self._5()); - private final P1 f = P1.memo(u -> self._6()); - private final P1 g = P1.memo(u -> self._7()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); + private final P1 d = weakMemo(self::_4); + private final P1 e = weakMemo(self::_5); + private final P1 f = weakMemo(self::_6); + private final P1 g = weakMemo(self::_7); public A _1() { return a._1(); @@ -437,11 +447,7 @@ public G _7() { * @return A function that returns the first element of a product. */ public static fj.F, A> __1() { - return new fj.F, A>() { - public A f(final P7 p) { - return p._1(); - } - }; + return P7::_1; } /** @@ -450,11 +456,7 @@ public A f(final P7 p) { * @return A function that returns the second element of a product. */ public static fj.F, B> __2() { - return new fj.F, B>() { - public B f(final P7 p) { - return p._2(); - } - }; + return P7::_2; } /** @@ -463,11 +465,7 @@ public B f(final P7 p) { * @return A function that returns the third element of a product. */ public static fj.F, C> __3() { - return new fj.F, C>() { - public C f(final P7 p) { - return p._3(); - } - }; + return P7::_3; } /** @@ -476,11 +474,7 @@ public C f(final P7 p) { * @return A function that returns the fourth element of a product. */ public static fj.F, D> __4() { - return new fj.F, D>() { - public D f(final P7 p) { - return p._4(); - } - }; + return P7::_4; } /** @@ -489,11 +483,7 @@ public D f(final P7 p) { * @return A function that returns the fifth element of a product. */ public static fj.F, E> __5() { - return new fj.F, E>() { - public E f(final P7 p) { - return p._5(); - } - }; + return P7::_5; } /** @@ -502,11 +492,7 @@ public E f(final P7 p) { * @return A function that returns the sixth element of a product. */ public static fj.F, F$> __6() { - return new fj.F, F$>() { - public F$ f(final P7 p) { - return p._6(); - } - }; + return P7::_6; } /** @@ -515,26 +501,22 @@ public E f(final P7 p) { * @return A function that returns the seventh element of a product. */ public static fj.F, G> __7() { - return new fj.F, G>() { - public G f(final P7 p) { - return p._7(); - } - }; + return P7::_7; } @Override - public String toString() { + public final String toString() { return Show.p7Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P7.class, this, other, - () -> Equal.p7Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p7Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p7Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } diff --git a/core/src/main/java/fj/P8.java b/core/src/main/java/fj/P8.java index d9e9f42c..571d6a15 100644 --- a/core/src/main/java/fj/P8.java +++ b/core/src/main/java/fj/P8.java @@ -1,11 +1,11 @@ package fj; +import static fj.P.weakMemo; + /** * A product-8. - * - * @version %build.number% */ -@SuppressWarnings({"UnnecessaryFullyQualifiedName"}) +@SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P8 { /** * Access the first element of the product. @@ -406,7 +406,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); } /** @@ -415,7 +415,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); } /** @@ -424,7 +424,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); } /** @@ -433,7 +433,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); } /** @@ -442,7 +442,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); } /** @@ -451,7 +451,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); } /** @@ -460,7 +460,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); } /** @@ -469,7 +469,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); } /** @@ -480,14 +480,14 @@ public final P1 _8_() { public final P8 memo() { P8 self = this; return new P8() { - private final P1 a = P1.memo(u -> self._1()); - private final P1 b = P1.memo(u -> self._2()); - private final P1 c = P1.memo(u -> self._3()); - private final P1 d = P1.memo(u -> self._4()); - private final P1 e = P1.memo(u -> self._5()); - private final P1 f = P1.memo(u -> self._6()); - private final P1 g = P1.memo(u -> self._7()); - private final P1 h = P1.memo(u -> self._8()); + private final P1 a = weakMemo(self::_1); + private final P1 b = weakMemo(self::_2); + private final P1 c = weakMemo(self::_3); + private final P1 d = weakMemo(self::_4); + private final P1 e = weakMemo(self::_5); + private final P1 f = weakMemo(self::_6); + private final P1 g = weakMemo(self::_7); + private final P1 h = weakMemo(self::_8); public A _1() { return a._1(); @@ -530,11 +530,7 @@ public H _8() { * @return A function that returns the first element of a product. */ public static fj.F, A> __1() { - return new fj.F, A>() { - public A f(final P8 p) { - return p._1(); - } - }; + return P8::_1; } /** @@ -543,11 +539,7 @@ public A f(final P8 p) { * @return A function that returns the second element of a product. */ public static fj.F, B> __2() { - return new fj.F, B>() { - public B f(final P8 p) { - return p._2(); - } - }; + return P8::_2; } /** @@ -556,11 +548,7 @@ public B f(final P8 p) { * @return A function that returns the third element of a product. */ public static fj.F, C> __3() { - return new fj.F, C>() { - public C f(final P8 p) { - return p._3(); - } - }; + return P8::_3; } /** @@ -569,11 +557,7 @@ public C f(final P8 p) { * @return A function that returns the fourth element of a product. */ public static fj.F, D> __4() { - return new fj.F, D>() { - public D f(final P8 p) { - return p._4(); - } - }; + return P8::_4; } /** @@ -582,11 +566,7 @@ public D f(final P8 p) { * @return A function that returns the fifth element of a product. */ public static fj.F, E> __5() { - return new fj.F, E>() { - public E f(final P8 p) { - return p._5(); - } - }; + return P8::_5; } /** @@ -595,11 +575,7 @@ public E f(final P8 p) { * @return A function that returns the sixth element of a product. */ public static fj.F, F$> __6() { - return new fj.F, F$>() { - public F$ f(final P8 p) { - return p._6(); - } - }; + return P8::_6; } /** @@ -608,11 +584,7 @@ public E f(final P8 p) { * @return A function that returns the seventh element of a product. */ public static fj.F, G> __7() { - return new fj.F, G>() { - public G f(final P8 p) { - return p._7(); - } - }; + return P8::_7; } /** @@ -621,26 +593,22 @@ public G f(final P8 p) { * @return A function that returns the eighth element of a product. */ public static fj.F, H> __8() { - return new fj.F, H>() { - public H f(final P8 p) { - return p._8(); - } - }; + return P8::_8; } @Override - public String toString() { + public final String toString() { return Show.p8Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } @Override - public boolean equals(Object other) { + public final boolean equals(Object other) { return Equal.equals0(P8.class, this, other, - () -> Equal.p8Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + () -> Equal.p8Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.p8Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); } 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 45f03c06..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(); @@ -10,12 +7,12 @@ public abstract class Rng { public abstract P2 nextLong(); // [low, high] inclusive - public P2 range(int low, int high) { + public final P2 range(int low, int high) { return nextNatural().map2(x -> (x % (high - low + 1)) + low); } - public P2 nextNatural() { + public final P2 nextNatural() { return nextInt().map2(x -> x < 0 ? -(x + 1) : x); } diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index e1e4fdf7..cd31e1e3 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -1,7 +1,9 @@ package fj; import fj.data.Array; +import fj.data.DList; import fj.data.List; +import fj.data.IO; import fj.data.Natural; import fj.data.NonEmptyList; import fj.data.Option; @@ -11,21 +13,87 @@ import java.math.BigDecimal; import java.math.BigInteger; -import static fj.Function.curry; +import static fj.Function.constant; +import static fj.Function.identity; +import static fj.Monoid.*; +import static fj.data.DList.listDList; /** * Implementations must satisfy the law of associativity: *
      *
    • Associativity; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))
    • *
    - * - * @version %build.number% */ public final class Semigroup
    { - private final F> sum; - private Semigroup(final F> sum) { - this.sum = sum; + /** + * Primitives functions of Semigroup: minimal definition and overridable methods. + */ + public interface Definition { + + A append(A a1, A a2); + + default F prepend(A a) { + return a2 -> append(a, a2); + } + + default A sum(A a, F0> as) { + return as.f().foldLeft(this::append, a); + } + + default A multiply1p(int n, A a) { + if (n <= 0) { + return a; + } + + A xTmp = a; + int yTmp = n; + A zTmp = a; + while (true) { + if ((yTmp & 1) == 1) { + zTmp = append(xTmp, zTmp); + if (yTmp == 1) { + return zTmp; + } + } + xTmp = append(xTmp, xTmp); + yTmp = (yTmp) >>> 1; + } + } + + default Definition dual() { + return new Definition(){ + + @Override + public A append(A a1, A a2) { + return Definition.this.append(a2, a1); + } + + @Override + public A multiply1p(int n, A a) { + return Definition.this.multiply1p(n, a); + } + }; + } + } + + /** + * Primitives functions of Semigroup: alternative minimal definition and overridable methods. + */ + public interface AltDefinition extends Definition { + @Override + F prepend(A a); + + @Override + default A append(A a1, A a2) { + return prepend(a1).f(a2); + } + } + + private final Definition def; + + private Semigroup(final Definition def) { + this.def = def; } /** @@ -36,7 +104,7 @@ private Semigroup(final F> sum) { * @return The of the two given arguments. */ public A sum(final A a1, final A a2) { - return sum.f(a1).f(a2); + return def.append(a1, a2); } /** @@ -46,7 +114,7 @@ public A sum(final A a1, final A a2) { * @return A function that sums the given value according to this semigroup. */ public F sum(final A a1) { - return sum.f(a1); + return def.prepend(a1); } /** @@ -55,13 +123,13 @@ public F sum(final A a1) { * @return A function that sums according to this semigroup. */ public F> sum() { - return sum; + return def::prepend; } /** * 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 @@ -70,197 +138,332 @@ public F> sum() { * {@code zero()} */ public A multiply1p(int n, A a) { - return multiply1p(sum, n, a); + return def.multiply1p(n, a); } - // shared implementation between Semigroup and Monoid - static A multiply1p(F> sum, int n, A a) { - if (n <= 0) { - return a; - } - A xTmp = a; - int yTmp = n; - A zTmp = a; - while (true) { - if ((yTmp & 1) == 1) { - zTmp = sum.f(xTmp).f(zTmp); - if (yTmp == 1) { - return zTmp; - } + /** + * Sums the given values with left-fold. + */ + public A sumNel(final NonEmptyList as) { + return as.foldLeft1(def::append); + } + + /** + * Sums the given values with left-fold, shortcutting the computation as early as possible. + */ + public A sumStream(A a, F0> as) { + return def.sum(a, as); + } + + /** + * Swaps the arguments when summing. + */ + public Semigroup dual() { + return semigroupDef(def.dual()); + } + + /** + * Lifts the semigroup to obtain a trivial monoid. + */ + public Monoid> lift() { + return Monoid.optionMonoid(this); + } + + /** + * Maps the given functions across this monoid as an invariant functor. + * + * @param f The covariant map. + * @param g The contra-variant map. + * @return A new monoid. + */ + public Semigroup xmap(final F f, final F g) { + Definition def = this.def; + return semigroupDef(new Definition() { + + @Override + public B append(B a1, B a2) { + return f.f(def.append(g.f(a1), g.f(a2))); } - xTmp = sum.f(xTmp).f(xTmp); - yTmp = (yTmp) >>> 1; - } + + @Override + public F prepend(B b) { + return def.prepend(g.f(b)).dimap(g, f); + } + + @Override + public B multiply1p(int n, B b) { + return f.f(def.multiply1p(n , g.f(b))); + } + + @Override + public B sum(B b, F0> bs) { + return f.f(def.sum(g.f(b), () -> bs.f().map(g))); + } + }); + } + + public Semigroup compose(Semigroup sb, final F b, final F a, final F2 c) { + Definition saDef = this.def; + Definition sbDef = sb.def; + return semigroupDef(new Definition() { + + @Override + public C append(C c1, C c2) { + return c.f(saDef.append(a.f(c1), a.f(c2)), sbDef.append(b.f(c1), b.f(c2))); + } + + @Override + public F prepend(C c1) { + F prependA = saDef.prepend(a.f(c1)); + F prependB = sbDef.prepend(b.f(c1)); + return c2 -> c.f(prependA.f(a.f(c2)), prependB.f(b.f(c2))); + } + + @Override + public C multiply1p(int n, C c1) { + return c.f(saDef.multiply1p(n, a.f(c1)), sbDef.multiply1p(n, b.f(c1))); + } + + @Override + public C sum(C c1, F0> cs) { + return c.f(saDef.sum(a.f(c1), () -> cs.f().map(a)), sbDef.sum(b.f(c1), () -> cs.f().map(b))); + } + }); + } + + /** + * Constructs a monoid from this semigroup and a zero value, which must follow the monoidal laws. + * + * @param zero The zero for the monoid. + * @return A monoid instance that uses the given sun function and zero value. + */ + public Monoid monoid(A zero) { + return monoidDef(this.def, zero); + } + + /** + * Constructs a semigroup from the given definition. + * + * @param def The definition to construct this semigroup with. + * @return A semigroup from the given definition. + */ + public static Semigroup semigroupDef(final Definition def) { + return new Semigroup<>(def); + } + + /** + * Constructs a semigroup from the given definition. + * + * @param def The definition to construct this semigroup with. + * @return A semigroup from the given definition. + */ + public static Semigroup semigroupDef(final AltDefinition def) { + return new Semigroup<>(def); } /** * Constructs a semigroup from the given function. + * Java 8+ users: use {@link #semigroupDef(AltDefinition)} instead. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static Semigroup semigroup(final F> sum) { - return new Semigroup(sum); + return semigroupDef(sum::f); } /** * Constructs a semigroup from the given function. + * Java 8+ users: use {@link #semigroupDef(Definition)} instead. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static Semigroup semigroup(final F2 sum) { - return new Semigroup(curry(sum)); + return new Semigroup<>(sum::f); } - /** - * A semigroup that adds integers. - */ - public static final Semigroup intAdditionSemigroup = semigroup((i1, i2) -> i1 + i2); /** - * A semigroup that adds doubles. + * A semigroup that adds integers. */ - public static final Semigroup doubleAdditionSemigroup = semigroup((d1, d2) -> d1 + d2); + public static final Semigroup intAdditionSemigroup = intAdditionMonoid.semigroup(); /** * A semigroup that multiplies integers. */ - public static final Semigroup intMultiplicationSemigroup = semigroup((i1, i2) -> i1 * i2); - - /** - * A semigroup that multiplies doubles. - */ - public static final Semigroup doubleMultiplicationSemigroup = semigroup((d1, d2) -> d1 * d2); + public static final Semigroup intMultiplicationSemigroup = intMultiplicationMonoid.semigroup(); /** * A semigroup that yields the maximum of integers. */ - public static final Semigroup intMaximumSemigroup = semigroup(Ord.intOrd.max); + public static final Semigroup intMaximumSemigroup = intMaxMonoid.semigroup(); /** * A semigroup that yields the minimum of integers. */ - public static final Semigroup intMinimumSemigroup = semigroup(Ord.intOrd.min); + public static final Semigroup intMinimumSemigroup = intMinMonoid.semigroup(); /** * A semigroup that adds big integers. */ - public static final Semigroup bigintAdditionSemigroup = - semigroup((i1, i2) -> i1.add(i2)); + public static final Semigroup bigintAdditionSemigroup = bigintAdditionMonoid.semigroup(); /** * A semigroup that multiplies big integers. */ public static final Semigroup bigintMultiplicationSemigroup = - semigroup((i1, i2) -> i1.multiply(i2)); + semigroup(BigInteger::multiply); /** * A semigroup that yields the maximum of big integers. */ - public static final Semigroup bigintMaximumSemigroup = semigroup(Ord.bigintOrd.max); + public static final Semigroup bigintMaximumSemigroup = Ord.bigintOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big integers. */ - public static final Semigroup bigintMinimumSemigroup = semigroup(Ord.bigintOrd.min); + public static final Semigroup bigintMinimumSemigroup = Ord.bigintOrd.minSemigroup(); /** * A semigroup that adds big decimals. */ - public static final Semigroup bigdecimalAdditionSemigroup = - semigroup((i1, i2) -> i1.add(i2)); + public static final Semigroup bigdecimalAdditionSemigroup = bigdecimalAdditionMonoid.semigroup(); /** * A semigroup that multiplies big decimals. */ - public static final Semigroup bigdecimalMultiplicationSemigroup = - semigroup((i1, i2) -> i1.multiply(i2)); + public static final Semigroup bigdecimalMultiplicationSemigroup = bigdecimalMultiplicationMonoid.semigroup(); /** * A semigroup that yields the maximum of big decimals. */ - public static final Semigroup bigDecimalMaximumSemigroup = semigroup(Ord.bigdecimalOrd.max); + public static final Semigroup bigDecimalMaximumSemigroup = Ord.bigdecimalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big decimals. */ - public static final Semigroup bigDecimalMinimumSemigroup = semigroup(Ord.bigdecimalOrd.min); + public static final Semigroup bigDecimalMinimumSemigroup = Ord.bigdecimalOrd.minSemigroup(); /** * A semigroup that multiplies natural numbers. */ - public static final Semigroup naturalMultiplicationSemigroup = - semigroup((n1, n2) -> n1.multiply(n2)); + public static final Semigroup naturalMultiplicationSemigroup = naturalMultiplicationMonoid.semigroup(); /** - * A semigroup that multiplies natural numbers. + * A semigroup that adds natural numbers. */ - public static final Semigroup naturalAdditionSemigroup = - semigroup((n1, n2) -> n1.add(n2)); + public static final Semigroup naturalAdditionSemigroup = naturalAdditionMonoid.semigroup(); /** * A semigroup that yields the maximum of natural numbers. */ - public static final Semigroup naturalMaximumSemigroup = semigroup(Ord.naturalOrd.max); + public static final Semigroup naturalMaximumSemigroup = Ord.naturalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of natural numbers. */ - public static final Semigroup naturalMinimumSemigroup = semigroup(Ord.naturalOrd.min); + public static final Semigroup naturalMinimumSemigroup = Ord.naturalOrd.minSemigroup(); /** * A semigroup that adds longs. */ - public static final Semigroup longAdditionSemigroup = semigroup((x, y) -> x + y); + public static final Semigroup longAdditionSemigroup = longAdditionMonoid.semigroup(); /** * A semigroup that multiplies longs. */ - public static final Semigroup longMultiplicationSemigroup = semigroup((x, y) -> x * y); + public static final Semigroup longMultiplicationSemigroup = longMultiplicationMonoid.semigroup(); /** * A semigroup that yields the maximum of longs. */ - public static final Semigroup longMaximumSemigroup = semigroup(Ord.longOrd.max); + public static final Semigroup longMaximumSemigroup = Ord.longOrd.maxSemigroup(); /** * A semigroup that yields the minimum of longs. */ - public static final Semigroup longMinimumSemigroup = semigroup(Ord.longOrd.min); + public static final Semigroup longMinimumSemigroup = Ord.longOrd.minSemigroup(); /** * A semigroup that ORs booleans. */ - public static final Semigroup disjunctionSemigroup = semigroup((b1, b2) -> b1 || b2); + public static final Semigroup disjunctionSemigroup = disjunctionMonoid.semigroup(); /** * A semigroup that XORs booleans. */ - public static final Semigroup exclusiveDisjunctionSemiGroup = semigroup((p, q) -> p && !q || !p && q); + public static final Semigroup exclusiveDisjunctionSemiGroup = exclusiveDisjunctionMonoid.semigroup(); /** * A semigroup that ANDs booleans. */ - public static final Semigroup conjunctionSemigroup = semigroup((b1, b2) -> b1 && b2); + public static final Semigroup conjunctionSemigroup = conjunctionMonoid.semigroup(); /** * A semigroup that appends strings. */ - public static final Semigroup stringSemigroup = semigroup((s1, s2) -> s1 + s2); + public static final Semigroup stringSemigroup = stringMonoid.semigroup(); /** * A semigroup that appends string buffers. */ - public static final Semigroup stringBufferSemigroup = - semigroup((s1, s2) -> new StringBuffer(s1).append(s2)); + public static final Semigroup stringBufferSemigroup = stringBufferMonoid.semigroup(); /** * A semigroup that appends string builders. */ - public static final Semigroup stringBuilderSemigroup = - semigroup((s1, s2) -> new StringBuilder(s1).append(s2)); + public static final Semigroup stringBuilderSemigroup = stringBuilderMonoid.semigroup(); + + /** + * A semigroup which always uses the "first" (left-hand side) value. + */ + public static Semigroup firstSemigroup() { + return semigroupDef(new Definition() { + @Override + public A append(A a1, A a2) { + return a1; + } + + @Override + public F prepend(A a) { + return constant(a); + } + + @Override + public A multiply1p(int n, A a) { + return a; + } + + @Override + public A sum(A a, F0> as) { + return a; + } + }); + } + + /** + * A semigroup which always uses the "last" (right-hand side) value. + */ + public static Semigroup lastSemigroup() { + return semigroupDef(new Definition() { + @Override + public A append(A a1, A a2) { + return a2; + } + + @Override + public F prepend(A a) { + return identity(); + } + + @Override + public A multiply1p(int n, A a) { + return a; + } + }); + } /** * A semigroup for functions. @@ -269,7 +472,8 @@ public static Semigroup semigroup(final F2 sum) { * @return A semigroup for functions. */ public static Semigroup> functionSemigroup(final Semigroup sb) { - return semigroup((a1, a2) -> a -> sb.sum(a1.f(a), a2.f(a))); + Definition sbDef = sb.def; + return semigroupDef((a1, a2) -> a -> sbDef.append(a1.f(a), a2.f(a))); } /** @@ -278,7 +482,7 @@ public static Semigroup> functionSemigroup(final Semigroup sb) * @return A semigroup for lists. */ public static Semigroup> listSemigroup() { - return semigroup((a1, a2) -> a1.append(a2)); + return Monoid.listMonoid().semigroup(); } /** @@ -287,15 +491,19 @@ public static Semigroup> listSemigroup() { * @return A semigroup for non-empty lists. */ public static Semigroup> nonEmptyListSemigroup() { - return semigroup((a1, a2) -> a1.append(a2)); - } - /** - * A semigroup for optional values. - ** @return A semigroup for optional values. - */ - public static Semigroup> optionSemigroup() { - return semigroup((a1, a2) -> a1.isSome() ? a1 : a2); + return semigroupDef(new Definition>() { + @Override + public NonEmptyList append(NonEmptyList a1, NonEmptyList a2) { + return a1.append(a2); + } + + @Override + public NonEmptyList sum(NonEmptyList nea, F0>> neas) { + List tail = neas.f().map(nel -> listDList(nel.toList())).foldLeft(DList::append, DList.nil()).run(); + return nea.append(tail); + } + }); } /** @@ -304,7 +512,7 @@ public static Semigroup> optionSemigroup() { * @return A semigroup for optional values that take the first available value. */ public static Semigroup> firstOptionSemigroup() { - return semigroup((a1, a2) -> a1.orElse(a2)); + return Monoid.firstOptionMonoid().semigroup(); } /** @@ -313,7 +521,7 @@ public static Semigroup> firstOptionSemigroup() { * @return A semigroup for optional values that take the last available value. */ public static Semigroup> lastOptionSemigroup() { - return semigroup((a1, a2) -> a2.orElse(a1)); + return Monoid.lastOptionMonoid().semigroup(); } /** @@ -322,7 +530,7 @@ public static Semigroup> lastOptionSemigroup() { * @return A semigroup for streams. */ public static Semigroup> streamSemigroup() { - return semigroup((a1, a2) -> a1.append(a2)); + return Monoid.streamMonoid().semigroup(); } /** @@ -331,42 +539,75 @@ public static Semigroup> streamSemigroup() { * @return A semigroup for arrays. */ public static Semigroup> arraySemigroup() { - return semigroup((a1, a2) -> a1.append(a2)); + return Monoid.arrayMonoid().semigroup(); } /** - * A semigroup for unary products. + * A lazy semigroup for unary products. * * @param sa A semigroup for the product's type. * @return A semigroup for unary products. */ public static Semigroup> p1Semigroup(final Semigroup sa) { - return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()))); + Definition def = sa.def; + return semigroupDef(new Definition>() { + @Override + public P1 append(P1 a1, P1 a2) { + return P.lazy(() -> def.append(a1._1(), a2._1())); + } + + @Override + public P1 multiply1p(int n, P1 ap1) { + return P.lazy(() -> def.multiply1p(n, ap1._1())); + } + + @Override + public P1 sum(P1 ap1, F0>> as) { + return P.lazy(() -> def.sum(ap1._1(), () -> as.f().map(P1.__1()))); + } + }); } /** - * A semigroup for binary products. + * A lazy semigroup for binary products. * * @param sa A semigroup for the product's first type. * @param sb A semigroup for the product's second type. * @return A semigroup for binary products. */ public static Semigroup> p2Semigroup(final Semigroup sa, final Semigroup sb) { - return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); + return semigroupDef((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); + } + + /** + * A semigroup for IO values. + */ + public static Semigroup> ioSemigroup(final Semigroup sa) { + Definition def = sa.def; + return semigroupDef((a1, a2) -> () -> def.append(a1.run(), a2.run())); } /** * A semigroup for the Unit value. */ - public static final Semigroup unitSemigroup = semigroup((u1, u2) -> Unit.unit()); + public static final Semigroup unitSemigroup = unitMonoid.semigroup(); /** - * A semigroup for sets. + * A union semigroup for sets. * * @return a semigroup for sets. */ public static Semigroup> setSemigroup() { - return semigroup((a, b) -> a.union(b)); + return semigroupDef(Set::union); + } + + /** + * A intersection semigroup for sets. + * + * @return a semigroup for sets. + */ + public static Semigroup> setIntersectionSemigroup() { + return semigroupDef(Set::intersect); } } diff --git a/core/src/main/java/fj/Show.java b/core/src/main/java/fj/Show.java index b67f662a..8a21a281 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -1,6 +1,9 @@ package fj; import fj.data.*; +import fj.data.hamt.BitSet; +import fj.data.hamt.HashArrayMappedTrie; +import fj.data.fingertrees.FingerTree; import fj.data.hlist.HList; import fj.data.vector.V2; import fj.data.vector.V3; @@ -9,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; @@ -23,8 +27,6 @@ /** * Renders an object for display. - * - * @version %build.number% */ public final class Show { private final F> f; @@ -79,7 +81,7 @@ public String showS(final A a) { * @return the transformation equivalent to this show. */ public F showS_() { - return a -> showS(a); + return this::showS; } /** @@ -159,7 +161,7 @@ public static Show showS(final F f) { * @return A show instance that uses {@link Object#toString()} to perform the display rendering. */ public static Show anyShow() { - return show(a -> Stream.fromString((a == null) ? "null" : a.toString())); + return show(a -> fromString((a == null) ? "null" : a.toString())); } /** @@ -252,6 +254,29 @@ public static Show> eitherShow(final Show sa, final Show< fromString("Right(").append(sb.f.f(e.right().value())).append(single(')'))); } + 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(')')), + 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. + * + * @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. * @@ -282,7 +307,7 @@ public static Show> listShow(final Show sa) { * @return A show instance for the {@link NonEmptyList} type. */ public static Show> nonEmptyListShow(final Show sa) { - return listShow(sa).contramap(NonEmptyList.toList_()); + return listShow(sa).contramap(NonEmptyList.toList_()); } /** @@ -301,6 +326,44 @@ public static Show> treeShow(final Show sa) { }); } + public static Show> digitShow(final Show sv, final Show sa) { + return show(d -> { + String s = d.match( + o -> "One(" + o.measure() + " -> " + o.value() + ")", + two -> "Two(" + two.measure() + " -> " + v2Show(sa).showS(two.values()) + ")", + three -> "Three(" + three.measure() + " -> " + v3Show(sa).showS(three.values()) + ")", + four -> "Four(" + four.measure() + " -> " + v4Show(sa).showS(four.values()) + ")" + ); + return Stream.fromString(s); + }); + } + + public static Show> nodeShow(final Show sv, final Show sa) { + return show(n -> { + final String s = n.match( + n2 -> "Node2(" + n2.measure() + " -> " + v2Show(sa).showS(n2.toVector()) + ")", + n3 -> "Node3(" + n3.measure() + " -> " + v3Show(sa).showS(n3.toVector()) + ")"); + return Stream.fromString(s); + }); + } + + public static Show> fingerTreeShow(final Show sv, final Show sa) { + + return show(ft -> { + String sep = ", "; + String str = ft.match(e -> "Empty()", + s -> "Single(" + sv.showS(ft.measure()) + " -> " + sa.showS(s.value()) + ")", + d -> "Deep(" + d.measure() + " -> " + + digitShow(sv, sa).showS(d.prefix()) + sep + + fingerTreeShow(sv, nodeShow(sv, sa)).showS(d.middle()) + sep + + digitShow(sv, sa).showS(d.suffix()) + + ")" + ); + return Stream.fromString(str); + }); + } + + public static Show> seqShow(final Show sa) { return show(s -> streamShow(sa, "Seq(", ",", ")").show(s.toStream())); } @@ -325,7 +388,7 @@ public static Show> setShow(final Show sa) { public static Show> treeMapShow(final Show sk, final Show sv) { return show(tm -> { Stream> stream = Stream.iteratorStream(tm.iterator()); - return streamShow(Show.p2MapShow(sk, sv), "TreeMap(", ",", ")").show(stream); + return streamShow(p2MapShow(sk, sv), "TreeMap(", ",", ")").show(stream); }); } @@ -337,7 +400,7 @@ public static Show> treeMapShow(final Show sk, final Sho * @return A show instance for the {@link P2 tuple-2} type. */ public static Show> p2MapShow(final Show sa, final Show sb) { - return p2Show(sa, sb, "(", ":", ")"); + return p2Show(sa, sb, "(", ": ", ")"); } /** @@ -425,7 +488,7 @@ public static Show> p1Show(final Show sa) { } public static Show> p1ShowLazy(final Show sa) { - return show(p -> Stream.fromString("(?)")); + return show(p -> fromString("(?)")); } public static Show> p1ShowEager(final Show sa) { @@ -550,6 +613,12 @@ public static Show> p5Show(final Show sa, f .append(sg.show(p._7())).snoc(',').append(sh.show(p._8())).snoc(')')); } + public static Show> priorityQueueShow(Show sk, Show sv) { + return show(pq -> { + return streamShow(p2MapShow(sk, sv), "PriorityQueue(", ", ", ")").show(pq.toStream()); + }); + } + /** * A show instance for a vector-2. * @@ -557,7 +626,7 @@ public static Show> p5Show(final Show sa, f * @return A show instance for a vector-2. */ public static Show> v2Show(final Show ea) { - return streamShow(ea, "V2(", ",", ")").contramap(V2.toStream_()); + return streamShow(ea, "V2(", ",", ")").contramap(V2.toStream_()); } /** @@ -567,7 +636,7 @@ public static Show> v2Show(final Show ea) { * @return A show instance for a vector-3. */ public static Show> v3Show(final Show ea) { - return streamShow(ea, "V3(", ",", ")").contramap(V3.toStream_()); + return streamShow(ea, "V3(", ",", ")").contramap(V3.toStream_()); } /** @@ -577,7 +646,7 @@ public static Show> v3Show(final Show ea) { * @return A show instance for a vector-4. */ public static Show> v4Show(final Show ea) { - return streamShow(ea, "V4(", ",", ")").contramap(V4.toStream_()); + return streamShow(ea, "V4(", ",", ")").contramap(V4.toStream_()); } /** @@ -587,7 +656,7 @@ public static Show> v4Show(final Show ea) { * @return A show instance for a vector-5. */ public static Show> v5Show(final Show ea) { - return streamShow(ea, "V5(", ",", ")").contramap(V5.toStream_()); + return streamShow(ea, "V5(", ",", ")").contramap(V5.toStream_()); } /** @@ -597,7 +666,7 @@ public static Show> v5Show(final Show ea) { * @return A show instance for a vector-6. */ public static Show> v6Show(final Show ea) { - return streamShow(ea, "V6(", ",", ")").contramap(V6.toStream_()); + return streamShow(ea, "V6(", ",", ")").contramap(V6.toStream_()); } /** @@ -607,7 +676,7 @@ public static Show> v6Show(final Show ea) { * @return A show instance for a vector-7. */ public static Show> v7Show(final Show ea) { - return streamShow(ea, "V7(", ",", ")").contramap(V7.toStream_()); + return streamShow(ea, "V7(", ",", ")").contramap(V7.toStream_()); } /** @@ -617,7 +686,7 @@ public static Show> v7Show(final Show ea) { * @return A show instance for a vector-8. */ public static Show> v8Show(final Show ea) { - return streamShow(ea, "V8(", ",", ")").contramap(V8.toStream_()); + return streamShow(ea, "V8(", ",", ")").contramap(V8.toStream_()); } /** @@ -643,7 +712,7 @@ public static Show> unlineShow(final Show sa) { /** * A show instance for the empty heterogeneous Stream. */ - public static final Show HListShow = showS(Function.constant("Nil")); + public static final Show HListShow = showS(Function.constant("Nil")); /** * A show instance for heterogeneous Streams. @@ -655,4 +724,21 @@ public static Show> unlineShow(final Show sa) { public static > Show> HListShow(final Show e, final Show l) { return show(c -> fromString("HList(").append(e.show(c.head())).append(l.show(c.tail())).append(fromString(")"))); } + + public static Show> hamtNodeShow(Show sk, Show sv) { + F, String> f = n -> n.match(p -> p2Show(sk, sv).showS(p), h -> hamtShow(sk, sv).showS(h)); + return Show.showS(f); + } + + public static Show> hamtShow(Show sk, Show sv) { + return Show.showS(hamt -> + "HashArrayMappedTrie(" + Show.bitSetShow.showS(hamt.getBitSet()) + + ", " + Show.seqShow(Show.hamtNodeShow(sk, sv)).showS(hamt.getSeq()) + ")" + ); + } + + public static final Show bitSetShow = Show.showS( + bs -> "BitSet(" + bs.asString() + ")" + ); + } diff --git a/core/src/main/java/fj/Try.java b/core/src/main/java/fj/Try.java index 5ed2673f..8418a621 100644 --- a/core/src/main/java/fj/Try.java +++ b/core/src/main/java/fj/Try.java @@ -10,10 +10,10 @@ import static fj.data.Validation.fail; import static fj.data.Validation.success; -/** - * Created by mperry on 24/07/2014. - */ -public class Try { +public final class Try { + + private Try() { + } /** * Promotes the Try0 to a Validation that returns an Exception on the failure side and its result on the success side. @@ -22,7 +22,7 @@ public class Try { * @return A Validation with an Exception on the failure side and its result on the success side. */ @SuppressWarnings("unchecked") - static public P1> f(final Try0 t) { + public static P1> f(final Try0 t) { return P.lazy(() -> { try { return success(t.f()); @@ -39,10 +39,10 @@ static public P1> f(final Try0 t * @return A Validation with an Exception on the failure side and its result on the success side. */ @SuppressWarnings("unchecked") - static public F> f(final Try1 t) { + public static F> f(final Try1 t) { return a -> { try { - return Validation.success(t.f(a)); + return success(t.f(a)); } catch (Exception e) { return fail((E) e); } @@ -56,7 +56,7 @@ static public F> f(final Try1 F2> f(final Try2 t) { + public static F2> f(final Try2 t) { return (a, b) -> { try { return success(t.f(a, b)); @@ -73,7 +73,7 @@ static public F2> f(final * @return A Validation with an Exception on the failure side and its result on the success side. */ @SuppressWarnings("unchecked") - static public F3> f(final Try3 t) { + public static F3> f(final Try3 t) { return (a, b, c) -> { try { return success(t.f(a, b, c)); @@ -90,7 +90,7 @@ static public F3> f( * @return A Validation with an Exception on the failure side and its result on the success side. */ @SuppressWarnings("unchecked") - static public F4> f(final Try4 t) { + public static F4> f(final Try4 t) { return (a, b, c, d) -> { try { return success(t.f(a, b, c, d)); @@ -107,7 +107,7 @@ static public F4 F5> f(final Try5 t) { + public static F5> f(final Try5 t) { return (a, b, c, d, e) -> { try { return success(t.f(a, b, c, d, e)); @@ -124,7 +124,7 @@ static public F5 F6> f(final Try6 t) { + public static F6> f(final Try6 t) { return (a, b, c, d, e, f) -> { try { return success(t.f(a, b, c, d, e, f)); @@ -141,7 +141,7 @@ static public F6 F7> f(final Try7 t) { + public static F7> f(final Try7 t) { return (a, b, c, d, e, f, g) -> { try { return success(t.f(a, b, c, d, e, f, g)); diff --git a/core/src/main/java/fj/TryEffect.java b/core/src/main/java/fj/TryEffect.java index 02d4521f..a240e638 100644 --- a/core/src/main/java/fj/TryEffect.java +++ b/core/src/main/java/fj/TryEffect.java @@ -1,16 +1,9 @@ package fj; -import fj.data.IO; -import fj.data.IOFunctions; import fj.data.Validation; import fj.function.*; -import java.io.IOException; - -/** - * Created by mperry on 29/08/2014. - */ -public class TryEffect { +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 f609fb0f..5c787f04 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -9,16 +9,16 @@ /** * 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 { // A Normal Trampoline is either done or suspended, and is allowed to be a subcomputation of a Codense. // This is the pointed functor part of the Trampoline monad. - private static abstract class Normal extends Trampoline { + private abstract static class Normal extends Trampoline { public abstract R foldNormal(final F pure, final F>, R> k); - public Trampoline bind(final F> f) { + public final Trampoline bind(final F> f) { return codense(this, f); } } @@ -43,25 +43,21 @@ 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(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 // gets shifted into the continuation. public Either>, A> resume() { - return left(sub.resume().either(p -> { - return p.map(ot -> { - // WARNING: In JDK 8, update 25 (current version) the following code is a - // workaround for an internal JDK compiler error, likely due to - // https:bugs.openjdk.java.net/browse/JDK-8062253. - F, Trampoline> f = o -> o.foldNormal(o1 -> cont.f(o1), t -> t._1().bind(cont)); - F, Trampoline> g = c -> codense(c.sub, o -> c.cont.f(o).bind(cont)); - return ot.fold(f, g); - }); - }, o -> P.lazy(() -> cont.f(o)))); + return left(sub.resume().either(p -> p.map(ot -> + ot.fold( + o -> o.foldNormal(cont, t -> t._1().bind(cont)), + c -> codense(c.sub, o -> c.cont.f(o).bind(cont)) + ) + ), o -> P.lazy(() -> cont.f(o)))); } } @@ -109,15 +105,15 @@ public Either>, A> resume() { } @SuppressWarnings("unchecked") - protected static Codense codense(final Normal a, final F> k) { - return new Codense((Normal) a, (F>) k); + private static Codense codense(final Normal a, final F> k) { + return new Codense<>((Normal) a, (F>) k); } /** - * @return The first-class version of `pure`. + * @return The first-class version of {@code pure}. */ public static F> pure() { - return a -> pure(a); + return Trampoline::pure; } /** @@ -127,7 +123,17 @@ public static F> pure() { * @return A trampoline that results in the given value. */ public static Trampoline pure(final A a) { - return new Pure(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)); } /** @@ -137,14 +143,15 @@ public static Trampoline pure(final A a) { * @return A trampoline whose next step runs the given thunk. */ public static Trampoline suspend(final P1> a) { - return new Suspend(a); + return new Suspend<>(a); } + /** - * @return The first-class version of `suspend`. + * @return The first-class version of {@code suspend}. */ public static F>, Trampoline> suspend_() { - return trampolineP1 -> suspend(trampolineP1); + return Trampoline::suspend; } protected abstract R fold(final F, R> n, final F, R> gs); @@ -164,28 +171,28 @@ 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)); } /** - * @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 aTrampoline -> aTrampoline.resume(); + return Trampoline::resume; } /** @@ -201,7 +208,7 @@ public static F, Either>, A>> resume_() { * @return The end result of this computation. */ @SuppressWarnings("LoopStatementThatDoesntLoop") - public A run() { + public final A run() { Trampoline current = this; while (true) { final Either>, A> x = current.resume(); @@ -221,7 +228,7 @@ public A run() { * @return A new Trampoline after applying the given function through this Trampoline. */ public final Trampoline apply(final Trampoline> lf) { - return lf.bind(f -> map(f)); + return lf.bind(this::map); } /** @@ -254,23 +261,24 @@ public static F, F, Trampoline>> liftM2 * @return A new trampoline that runs this trampoline and the given trampoline simultaneously. */ @SuppressWarnings("LoopStatementThatDoesntLoop") - public Trampoline zipWith(final Trampoline b, final F2 f) { + public final Trampoline zipWith(final Trampoline b, final F2 f) { final Either>, A> ea = resume(); final Either>, 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)))))); + 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()) { for (final B y : eb.right()) { - return suspend(P.lazy(() -> 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)))); + 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/db/DB.java b/core/src/main/java/fj/control/db/DB.java index 8e8590b6..4e16710e 100644 --- a/core/src/main/java/fj/control/db/DB.java +++ b/core/src/main/java/fj/control/db/DB.java @@ -2,6 +2,7 @@ import fj.F; import fj.Function; +import fj.function.Try1; import java.sql.Connection; import java.sql.SQLException; @@ -35,6 +36,21 @@ public A run(final Connection c) { }; } + /** + * Constructs a database action as a function from a database connection to a value. + * + * @param t A function from a database connection to a value allowed to throw + * SQLException + * @return A database action representing the given function. + */ + public static DB db(final Try1 t){ + return new DB() { + public A run(final Connection c) throws SQLException { + return t.f(c); + } + }; + } + /** * Returns the callable-valued function projection of this database action. * @@ -103,6 +119,6 @@ public B run(final Connection c) throws SQLException { * @return A new database action equivalent to the result of the given action. */ public static DB join(final DB> a) { - return a.bind(Function.>identity()); + return a.bind(Function.identity()); } } diff --git a/core/src/main/java/fj/control/db/DbState.java b/core/src/main/java/fj/control/db/DbState.java index 1243c309..228dff37 100644 --- a/core/src/main/java/fj/control/db/DbState.java +++ b/core/src/main/java/fj/control/db/DbState.java @@ -94,18 +94,21 @@ public Unit run(final Connection c) throws SQLException { * @throws SQLException in case of a database error. */ public A run(final DB dba) throws SQLException { - final Connection c = pc.connect(); - c.setAutoCommit(false); - try { - final A a = dba.run(c); + try (Connection c = pc.connect()) { + c.setAutoCommit(false); + final A a; + try { + a = dba.run(c); + } catch (RuntimeException | SQLException e) { + try { + c.rollback(); + } catch (Exception re) { + e.addSuppressed(re); + } + throw e; + } terminal.run(c); return a; - } catch (SQLException e) { - c.rollback(); - throw e; - } - finally { - c.close(); } } } diff --git a/core/src/main/java/fj/control/parallel/Actor.java b/core/src/main/java/fj/control/parallel/Actor.java index 77d4fe2d..c5985c7c 100644 --- a/core/src/main/java/fj/control/parallel/Actor.java +++ b/core/src/main/java/fj/control/parallel/Actor.java @@ -33,17 +33,17 @@ public final class Actor { * as they are sent. */ public static Actor queueActor(final Strategy s, final Effect1 ea) { - return actor(Strategy.seqStrategy(), new Effect1() { + return actor(Strategy.seqStrategy(), new Effect1() { // Lock to ensure the actor only acts on one message at a time - AtomicBoolean suspended = new AtomicBoolean(true); + final AtomicBoolean suspended = new AtomicBoolean(true); // Queue to hold pending messages - ConcurrentLinkedQueue mbox = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue mbox = new ConcurrentLinkedQueue<>(); // Product so the actor can use its strategy (to act on messages in other threads, // to handle exceptions, etc.) - P1 processor = new P1() { + final P1 processor = new P1() { @Override public Unit _1() { // get next item from queue T a = mbox.poll(); @@ -69,13 +69,13 @@ public static Actor queueActor(final Strategy s, final Effect1 e } // If there are pending messages, use the strategy to run the processor - protected void work() { + void work() { if (!mbox.isEmpty() && suspended.compareAndSet(true, false)) { s.par(processor); } } }); - }; + } private Actor(final Strategy s, final F> e) { this.s = s; @@ -90,7 +90,7 @@ private Actor(final Strategy s, final F> e) { * @return A new actor that uses the given parallelization strategy and has the given side-effect. */ public static Actor actor(final Strategy s, final Effect1 e) { - return new Actor(s, P1.curry(Effect.f(e))); + return new Actor<>(s, P1.curry(Effect.f(e))); } /** @@ -101,7 +101,7 @@ public static Actor actor(final Strategy s, final Effect1 e) { * @return A new actor that uses the given parallelization strategy and has the given side-effect. */ public static Actor actor(final Strategy s, final F> e) { - return new Actor(s, e); + return new Actor<>(s, e); } /** diff --git a/core/src/main/java/fj/control/parallel/Callables.java b/core/src/main/java/fj/control/parallel/Callables.java index 1b6114fe..995780c4 100644 --- a/core/src/main/java/fj/control/parallel/Callables.java +++ b/core/src/main/java/fj/control/parallel/Callables.java @@ -51,7 +51,7 @@ public static Callable callable(final Exception e) { * @return A function from a value to a Callable that completely preserves that value. */ public static F> callable() { - return a -> callable(a); + return Callables::callable; } /** @@ -72,7 +72,7 @@ public static F> callable(final F f) { * @return A transformation from a function to the equivalent Callable-valued function. */ public static F, F>> arrow() { - return f -> callable(f); + return Callables::callable; } /** @@ -93,7 +93,7 @@ public static Callable bind(final Callable a, final F F, Callable> fmap(final F f) { - return a -> Callables.bind(a, callable(f)); + return a -> bind(a, callable(f)); } /** @@ -126,7 +126,7 @@ public static Callable bind(final Callable ca, final Callable * @return A new Callable that is the join of the given Callable. */ public static Callable join(final Callable> a) { - return bind(a, Function.>identity()); + return bind(a, Function.identity()); } /** @@ -146,8 +146,7 @@ public static F, F, Callable>> liftM2(final * @return A single callable for the given List. */ public static Callable> sequence(final List> as) { - return as.foldRight(Callables., - List>liftM2(List.cons()), callable(List.nil())); + return as.foldRight(Callables.liftM2(List.cons()), callable(List.nil())); } /** @@ -156,7 +155,7 @@ public static Callable> sequence(final List> as) { * @return A function from a List of Callables to a single Callable of a List. */ public static F>, Callable>> sequence_() { - return as -> sequence(as); + return Callables::sequence; } /** @@ -181,7 +180,7 @@ public static P1> option(final Callable a) { * @return a function that turns a Callable into an optional value. */ public static F, P1>> option() { - return a -> option(a); + return Callables::option; } /** @@ -206,7 +205,7 @@ public static P1> either(final Callable a) { * @return a function that turns a Callable into an Either. */ public static F, P1>> either() { - return a -> either(a); + return Callables::either; } /** @@ -216,14 +215,12 @@ public static F, P1>> either() { * @return A Callable equivalent to the given Either value. */ public static Callable fromEither(final F0> e) { - return new Callable() { - public A call() throws Exception { - final Either e1 = e.f(); - if (e1.isLeft()) - throw e1.left().value(); - else - return e1.right().value(); - } + return () -> { + final Either e1 = e.f(); + if (e1.isLeft()) + throw e1.left().value(); + else + return e1.right().value(); }; } @@ -233,7 +230,7 @@ public A call() throws Exception { * @return a function that turns an Either into a Callable. */ public static F>, Callable> fromEither() { - return e -> fromEither(e); + return Callables::fromEither; } /** @@ -243,14 +240,12 @@ public static F>, Callable> fromEither() { * @return A Callable that yields some value or throws an exception in the case of no value. */ public static Callable fromOption(final F0> o) { - return new Callable() { - public A call() throws Exception { - final Option o1 = o.f(); - if (o1.isSome()) - return o1.some(); - else - throw new Exception("No value."); - } + return () -> { + final Option o1 = o.f(); + if (o1.isSome()) + return o1.some(); + else + throw new Exception("No value."); }; } @@ -261,7 +256,7 @@ public A call() throws Exception { * or throws an exception in the case of no value. */ public static F>, Callable> fromOption() { - return o -> fromOption(o); + return Callables::fromOption; } /** @@ -285,7 +280,7 @@ public static Callable normalise(final Callable a) { * @return A function that normalises the given Callable by calling it and wrapping the result in a new Callable. */ public static F, Callable> normalise() { - return a -> normalise(a); + return Callables::normalise; } } diff --git a/core/src/main/java/fj/control/parallel/ParModule.java b/core/src/main/java/fj/control/parallel/ParModule.java index 7d01139a..b9eeacbf 100644 --- a/core/src/main/java/fj/control/parallel/ParModule.java +++ b/core/src/main/java/fj/control/parallel/ParModule.java @@ -56,7 +56,7 @@ public Promise promise(final P1 p) { * @return a function that evaluates a given product concurrently and returns a Promise of the result. */ public F, Promise> promise() { - return ap1 -> promise(ap1); + return this::promise; } /** @@ -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); } /** @@ -77,7 +77,7 @@ public F> promise(final F f) { * @return A higher-order function that takes pure functions to promise-valued functions. */ public F, F>> promisePure() { - return abf -> promise(abf); + return this::promise; } /** @@ -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)); } @@ -110,7 +110,7 @@ public Actor effect(final Effect1 e) { * @return A function that takes an effect and returns a concurrent effect. */ public F, Actor> effect() { - return effect -> effect(effect); + return this::effect; } /** @@ -129,7 +129,7 @@ public Actor actor(final Effect1 e) { * @return A function that takes an effect and returns an actor that processes messages in some order. */ public F, Actor> actor() { - return effect -> actor(effect); + return this::actor; } /** @@ -148,7 +148,7 @@ public Promise> sequence(final List> ps) { * @return A first-class function that traverses a list inside a promise. */ public F>, Promise>> sequenceList() { - return list -> sequence(list); + return this::sequence; } /** @@ -167,7 +167,7 @@ public Promise> sequence(final Stream> ps) { * @return A first-class function that traverses a stream inside a promise. */ public F>, Promise>> sequenceStream() { - return stream -> sequence(stream); + return this::sequence; } /** @@ -261,7 +261,7 @@ public F, F, Promise>>> parMapList() { * @return A Promise of a new NonEmptyList with the given function applied to each element. */ public Promise> parMap(final NonEmptyList as, final F f) { - return mapM(as.toList(), promise(f)).fmap((F, NonEmptyList>) list -> NonEmptyList.fromList(list).some()); + return mapM(as.toList(), promise(f)).fmap(list -> NonEmptyList.fromList(list).some()); } /** @@ -293,7 +293,7 @@ public F, F, Promise>>> parMapStream() { */ public Promise> parMap(final Iterable as, final F f) { return parMap(iterableStream(as), f) - .fmap(Function., Iterable>vary(Function.>identity())); + .fmap(Function.vary(Function.identity())); } /** @@ -313,7 +313,7 @@ public F, F, Promise>>> parMapIterable() * @return A Promise of a new Array with the given function applied to each element. */ public Promise> parMap(final Array as, final F f) { - return parMap(as.toStream(), f).fmap(stream -> stream.toArray()); + return parMap(as.toStream(), f).fmap(Stream::toArray); } /** @@ -334,7 +334,7 @@ public F, F, Promise>>> parMapArray() { */ public Promise> parMap(final Zipper za, final F f) { return parMap(za.rights(), f) - .apply(promise(f).f(za.focus()).apply(parMap(za.lefts(), f).fmap(curry(Zipper.zipper())))); + .apply(promise(f).f(za.focus()).apply(parMap(za.lefts(), f).fmap(curry(Zipper.zipper())))); } /** @@ -346,7 +346,7 @@ public Promise> parMap(final Zipper za, final F f) { */ public Promise> parMap(final Tree ta, final F f) { return mapM(ta.subForest(), this., Tree>mapStream().f(this.parMapTree().f(f))) - .apply(promise(f).f(ta.root()).fmap(Tree.node())); + .apply(promise(f).f(ta.root()).fmap(Tree.node())); } /** @@ -370,8 +370,8 @@ public Promise> parMap(final TreeZipper za, final F, Stream>, Stream>, Stream>, A, Stream>>>> p = za.p(); return mapM(p._4(), p3 -> parMap(p3._3(), tf).apply(promise(f).f(p3._2()).apply( - parMap(p3._1(), tf).fmap(P.>, B, Stream>>p3())))).apply(parMap(za.rights(), tf).apply( - parMap(za.lefts(), tf).apply(parMap(p._1(), f).fmap(TreeZipper.treeZipper())))); + parMap(p3._1(), tf).fmap(P.p3())))).apply(parMap(za.rights(), tf).apply( + parMap(za.lefts(), tf).apply(parMap(p._1(), f).fmap(TreeZipper.treeZipper())))); } /** @@ -382,7 +382,7 @@ public Promise> parMap(final TreeZipper za, final F Promise> parFlatMap(final List as, final F> f) { - return parFoldMap(as, f, Monoid.listMonoid()); + return parFoldMap(as, f, Monoid.listMonoid()); } /** @@ -393,7 +393,7 @@ public Promise> parFlatMap(final List as, final F> * @return A promise of a new Stream with the given function bound across its elements. */ public Promise> parFlatMap(final Stream as, final F> f) { - return parFoldMap(as, f, Monoid.streamMonoid()); + return parFoldMap(as, f, Monoid.streamMonoid()); } /** @@ -404,7 +404,7 @@ public Promise> parFlatMap(final Stream as, final F Promise> parFlatMap(final Array as, final F> f) { - return parMap(as, f).fmap(Array.join()); + return parMap(as, f).fmap(Array.join()); } /** @@ -415,8 +415,8 @@ public Promise> parFlatMap(final Array as, final F Promise> parFlatMap(final Iterable as, final F> f) { - return parMap(as, f).fmap(IterableW.>join()) - .fmap(Function., Iterable>vary(Function.>identity())); + return parMap(as, f).fmap(IterableW.join()) + .fmap(Function.vary(Function.>identity())); } /** @@ -428,7 +428,7 @@ public Promise> parFlatMap(final Iterable as, final F Promise> parZipWith(final List as, final List bs, final F> f) { - return sequence(as.>zipWith(bs, promise(uncurryF2(f)))); + return sequence(as.zipWith(bs, promise(uncurryF2(f)))); } /** @@ -440,7 +440,7 @@ public Promise> parZipWith(final List as, final List bs, * @return A Promise of a new stream with the results of applying the given function across the two streams, stepwise. */ public Promise> parZipWith(final Stream as, final Stream bs, final F> f) { - return sequence(as.>zipWith(bs, promise(uncurryF2(f)))); + return sequence(as.zipWith(bs, promise(uncurryF2(f)))); } /** @@ -452,7 +452,7 @@ public Promise> parZipWith(final Stream as, final Stream< * @return A Promise of a new array with the results of applying the given function across the two arrays, stepwise. */ public Promise> parZipWith(final Array as, final Array bs, final F> f) { - return parZipWith(as.toStream(), bs.toStream(), f).fmap(stream -> stream.toArray()); + return parZipWith(as.toStream(), bs.toStream(), f).fmap(Stream::toArray); } /** @@ -465,7 +465,7 @@ public Promise> parZipWith(final Array as, final Array */ public Promise> parZipWith(final Iterable as, final Iterable bs, final F> f) { return parZipWith(iterableStream(as), iterableStream(bs), f).fmap( - Function., Iterable>vary(Function.>identity())); + Function.vary(Function.>identity())); } /** @@ -496,7 +496,7 @@ public Promise parFoldMap(final Stream as, final F map, final */ public Promise parFoldMap(final Stream as, final F map, final Monoid reduce, final F, P2, Stream>> chunking) { - return parMap(Stream.unfold(stream -> stream.isEmpty() ? Option., Stream>>none() : some(chunking.f(stream)), as), Stream.map_().f(map)).bind(stream -> parMap(stream, reduce.sumLeftS()).fmap(reduce.sumLeftS())); + return parMap(Stream.unfold(stream -> stream.isEmpty() ? Option.none() : some(chunking.f(stream)), as), Stream.map_().f(map)).bind(stream -> parMap(stream, reduce.sumLeftS()).fmap(reduce.sumLeftS())); } /** @@ -515,7 +515,7 @@ public Promise parFoldMap(final Stream as, final F map, final public Promise parFoldMap(final Iterable as, final F map, final Monoid reduce, final F, P2, Iterable>> chunking) { return parFoldMap(iterableStream(as), map, reduce, (Stream stream) -> { - final F, Stream> is = iterable -> iterableStream(iterable); + final F, Stream> is = Stream::iterableStream; return chunking.f(stream).map1(is).map2(is); }); } diff --git a/core/src/main/java/fj/control/parallel/Promise.java b/core/src/main/java/fj/control/parallel/Promise.java index 100b50f6..3689c286 100644 --- a/core/src/main/java/fj/control/parallel/Promise.java +++ b/core/src/main/java/fj/control/parallel/Promise.java @@ -37,7 +37,7 @@ public final class Promise { private final CountDownLatch l = new CountDownLatch(1); private volatile Option v = none(); - private final Queue> waiting = new LinkedList>(); + private final Queue> waiting = new LinkedList<>(); private Promise(final Strategy s, final Actor, Actor>, Promise>> qa) { this.s = s; @@ -62,7 +62,7 @@ public void f(final P2, Actor>, Promise> p) { p._1().right().value().act(snd.v.some()); } }); - return new Promise(s, q); + return new Promise<>(s, q); } /** @@ -75,7 +75,7 @@ public void f(final P2, Actor>, Promise> p) { */ public static Promise promise(final Strategy s, final P1 a) { final Promise p = mkPromise(s); - p.actor.act(P.p(Either., Actor>left(a), p)); + p.actor.act(p(Either.left(a), p)); return p; } @@ -118,7 +118,7 @@ public static F> promise(final Strategy s, final F a) { - actor.act(P.p(Either., Actor>right(a), this)); + actor.act(p(Either.right(a), this)); } /** @@ -176,7 +176,7 @@ public Promise bind(final F> f) { final Promise r = mkPromise(s); final Actor ab = actor(s, new Effect1() { public void f(final B b) { - r.actor.act(P.p(Either., Actor>left(P.p(b)), r)); + r.actor.act(p(Either.left(p(b)), r)); } }); to(ab.promise().contramap(f)); @@ -190,7 +190,7 @@ public void f(final B b) { * @return A new promise after applying the given promised function to this promise. */ public Promise apply(final Promise> pf) { - return pf.bind(f -> fmap(f)); + return pf.bind(this::fmap); } /** @@ -233,7 +233,7 @@ public static F, F, Promise>> liftM2(final F< * @return A single promise for the given List. */ public static Promise> sequence(final Strategy s, final List> as) { - return join(foldRight(s, liftM2(List.cons()), promise(s, P.p(List.nil()))).f(as)); + return join(foldRight(s, liftM2(List.cons()), promise(s, p(List.nil()))).f(as)); } /** @@ -254,7 +254,7 @@ public static F>, Promise>> sequence(final Strategy< * @return A single promise for the given Stream. */ public static Promise> sequence(final Strategy s, final Stream> as) { - return join(foldRightS(s, curry((Promise o, P1>> p) -> o.bind(a -> p._1().fmap(Stream.cons_().f(a)))), promise(s, P.p(Stream.nil()))).f(as)); + return join(foldRightS(s, curry((Promise o, P1>> p) -> o.bind(a -> p._1().fmap(Stream.cons_().f(a)))), promise(s, p(Stream.nil()))).f(as)); } /** @@ -275,7 +275,7 @@ public static F>, Promise>> sequenceS(final Strategy * @return A promised product. */ public static Promise> sequence(final Strategy s, final P1> p) { - return join(promise(s, p)).fmap(P.p1()); + return join(promise(s, p)).fmap(P.p1()); } /** @@ -289,7 +289,7 @@ public static Promise> sequence(final Strategy s, final P1 F, Promise> foldRight(final Strategy s, final F> f, final B b) { return new F, Promise>() { public Promise f(final List as) { - return as.isEmpty() ? promise(s, p(b)) : liftM2(f).f(promise(s, P.p(as.head()))).f( + return as.isEmpty() ? promise(s, p(b)) : liftM2(f).f(promise(s, p(as.head()))).f( join(s, P1.curry(this).f(as.tail()))); } }; @@ -307,8 +307,8 @@ public static F, Promise> foldRightS(final Strategy s, final B b) { return new F, Promise>() { public Promise f(final Stream as) { - return as.isEmpty() ? promise(s, P.p(b)) : liftM2(f).f(promise(s, P.p(as.head()))).f( - Promise.>join(s, P.lazy(() -> f(as.tail()._1()).fmap(P.p1())))); + return as.isEmpty() ? promise(s, p(b)) : liftM2(f).f(promise(s, p(as.head()))).f( + Promise.join(s, P.lazy(() -> f(as.tail()._1()).fmap(P.p1())))); } }; } @@ -381,7 +381,7 @@ public Promise> cojoin() { */ public Stream sequenceW(final Stream, B>> fs) { return fs.isEmpty() - ? Stream.nil() + ? Stream.nil() : Stream.cons(fs.head().f(this), () -> sequenceW(fs.tail()._1())); } diff --git a/core/src/main/java/fj/control/parallel/Strategy.java b/core/src/main/java/fj/control/parallel/Strategy.java index 28bd6d32..2b77a9e8 100644 --- a/core/src/main/java/fj/control/parallel/Strategy.java +++ b/core/src/main/java/fj/control/parallel/Strategy.java @@ -51,7 +51,7 @@ public F, P1> f() { * @return A strategy that uses the given function to evaluate product-1s. */ public static Strategy strategy(final F, P1> f) { - return new Strategy(f); + return new Strategy<>(f); } /** @@ -71,7 +71,7 @@ public P1 par(final P1 a) { * @return A function that executes concurrently when called, yielding a Future value. */ public F> concurry(final F f) { - return compose(f(), P1.curry(f)); + return compose(f(), P1.curry(f)); } /** @@ -91,7 +91,7 @@ public F>> concurry(final F2 f) { * @return A list of values extracted from the Futures in the argument list. */ public static List> mergeAll(final List> xs) { - return xs.map(Strategy.obtain()); + return xs.map(Strategy.obtain()); } /** @@ -136,7 +136,7 @@ public P1> parMap(final F f, final Array bs) { * @return A list with all of its elements transformed by the given function. */ public List parMap1(final F f, final List bs) { - return compose(P1.>__1(), parMapList(f)).f(bs); + return compose(P1.__1(), parMapList(f)).f(bs); } /** @@ -149,7 +149,7 @@ public List parMap1(final F f, final List bs) { * @return An array with all of its elements transformed by the given function. */ public Array parMap1(final F f, final Array bs) { - return compose(P1.>__1(), parMapArray(f)).f(bs); + return compose(P1.__1(), parMapArray(f)).f(bs); } /** @@ -168,7 +168,7 @@ public F, P1>> parMapList(final F f) { * @return A function that promotes another function to a parallel function on lists. */ public F, F, P1>>> parMapList() { - return f1 -> parMapList(f1); + return this::parMapList; } /** @@ -196,7 +196,7 @@ public F, P1>> parMapArray(final F f) { * @return A function that promotes another function to a parallel function on arrays. */ public F, F, P1>>> parMapArray() { - return f1 -> parMapArray(f1); + return this::parMapArray; } /** @@ -248,7 +248,7 @@ public static P1> parFlatMap(final Strategy> s, public static P1> parListChunk(final Strategy> s, final int chunkLength, final List> as) { - return P1.map_(List.join()).f(s.parList(as.partition(chunkLength).map(P1.sequenceList()))); + return P1.map_(List.join()).f(s.parList(as.partition(chunkLength).map(P1.sequenceList()))); } /** @@ -311,7 +311,7 @@ public F2, Array, P1>> parZipArrayWith(final F2 F, P1> obtain() { - return t -> obtain(t); + return Strategy::obtain; } /** @@ -339,9 +339,7 @@ public static P1 obtain(final Future t) { * @return An effect, which, given a Future, waits for it to obtain a value, discarding the value. */ public static Effect1> discard() { - return a -> { - Strategy.obtain().f(a)._1(); - }; + return a -> Strategy.obtain().f(a)._1(); } /** @@ -353,7 +351,7 @@ public static Effect1> discard() { */ public static Strategy simpleThreadStrategy() { return strategy(p -> { - final FutureTask t = new FutureTask(Java.P1_Callable().f(p)); + final FutureTask t = new FutureTask<>(Java.P1_Callable().f(p)); new Thread(t).start(); return obtain(t); }); @@ -398,7 +396,7 @@ public static Strategy seqStrategy() { * @return A strategy that performs no evaluation of its argument. */ public static Strategy idStrategy() { - return strategy(Function.>identity()); + return strategy(Function.identity()); } /** @@ -419,7 +417,7 @@ public Strategy xmap(final F, P1> f, final F, P1> g) { * @return A new strategy that applies the given transformation after each application of this strategy. */ public Strategy map(final F, P1> f) { - return xmap(f, Function.>identity()); + return xmap(f, Function.identity()); } /** @@ -429,7 +427,7 @@ public Strategy map(final F, P1> f) { * @return A new strategy that applies the given transformation before each application of this strategy. */ public Strategy contramap(final F, P1> f) { - return xmap(Function.>identity(), f); + return xmap(Function.identity(), f); } /** diff --git a/core/src/main/java/fj/data/$.java b/core/src/main/java/fj/data/$.java index 3ddf92d3..a1aa77bf 100644 --- a/core/src/main/java/fj/data/$.java +++ b/core/src/main/java/fj/data/$.java @@ -6,7 +6,7 @@ * The constant arrow, for attaching a new name to an existing type. For every pair of types A and B, this type * is the identity morphism from B to B. */ -@SuppressWarnings({"UnusedDeclaration"}) +@SuppressWarnings("UnusedDeclaration") public final class $ extends P1 { private final B b; @@ -17,20 +17,14 @@ 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); } public static $ constant(final B b) { - return new $(b); + return new $<>(b); } diff --git a/core/src/main/java/fj/data/Array.java b/core/src/main/java/fj/data/Array.java index f24e529c..fb245f3f 100755 --- a/core/src/main/java/fj/data/Array.java +++ b/core/src/main/java/fj/data/Array.java @@ -13,7 +13,9 @@ import java.util.AbstractCollection; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; @@ -88,7 +90,7 @@ public int length() { } public ImmutableProjection immutable() { - return new ImmutableProjection(this); + return new ImmutableProjection<>(this); } /** @@ -128,11 +130,6 @@ public Object[] array() { return copyOf(a, a.length); } - @SuppressWarnings("unchecked") - public A[] toJavaArray() { - return (A[]) array(); - } - /** * Returns an option projection of this array; None if empty, or the first element in * Some. @@ -141,7 +138,7 @@ public A[] toJavaArray() { */ @SuppressWarnings("unchecked") public Option toOption() { - return a.length == 0 ? Option.none() : some((A) a[0]); + return a.length == 0 ? Option.none() : some((A) a[0]); } /** @@ -153,7 +150,7 @@ public Option toOption() { */ @SuppressWarnings("unchecked") public Either toEither(final F0 x) { - return a.length == 0 ? Either. left(x.f()) : Either. right((A) a[0]); + return a.length == 0 ? Either.left(x.f()) : Either.right((A) a[0]); } /** @@ -180,7 +177,7 @@ public List toList() { @SuppressWarnings("unchecked") public Stream toStream() { return Stream.unfold(o -> - a.length > o ? some(p((A) a[o], o + 1)) : Option.>none(), 0 + a.length > o ? some(p((A) a[o], o + 1)) : Option.none(), 0 ); } @@ -195,7 +192,7 @@ public String toString() { * @param f The function to map across this array. * @return A new array after the given function has been applied to each element. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array map(final F f) { final Object[] bs = new Object[a.length]; @@ -203,7 +200,7 @@ public Array map(final F f) { bs[i] = f.f((A) a[i]); } - return new Array(bs); + return new Array<>(bs); } /** @@ -316,7 +313,7 @@ public B foldLeft(final F2 f, final B b) { * @param b The beginning value to start the application from. * @return The array containing all intermediate results of the left-fold reduction. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array scanLeft(final F> f, final B b) { final Object[] bs = new Object[a.length]; B x = b; @@ -326,7 +323,7 @@ public Array scanLeft(final F> f, final B b) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -350,7 +347,7 @@ public Array scanLeft(final F2 f, final B b) { * @param f The function to apply on each argument pair (next array element and first array element/previous result) * @return The array containing all intermediate results of the left-fold reduction. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array scanLeft1(final F> f) { final Object[] bs = new Object[a.length]; A x = get(0); @@ -361,7 +358,7 @@ public Array scanLeft1(final F> f) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -385,7 +382,7 @@ public Array scanLeft1(final F2 f) { * @param b The beginning value to start the application from. * @return The array containing all intermediate results of the right-fold reduction. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array scanRight(final F>f, final B b) { final Object[] bs = new Object[a.length]; B x = b; @@ -395,7 +392,7 @@ public Array scanRight(final F>f, final B b) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -419,7 +416,7 @@ public Array scanRight(final F2 f, final B b) { * @param f The function to apply on each argument pair (previous array element and last array element/previous result) * @return The array containing all intermediate results of the right-fold reduction. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array scanRight1(final F>f) { final Object[] bs = new Object[a.length]; A x = get(length() - 1); @@ -430,7 +427,7 @@ public Array scanRight1(final F>f) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -452,7 +449,7 @@ public Array scanRight1(final F2 f) { * @param f The function to apply to each element of this array. * @return A new array after performing the map, then final join. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public Array bind(final F> f) { List> x = List.nil(); int len = 0; @@ -475,7 +472,7 @@ public Unit f(final Array x) { } }); - return new Array(bs); + return new Array<>(bs); } /** @@ -520,7 +517,7 @@ public Array bind(final Array sb, final F2 f) { * @return A new array after applying the given array of functions through this array. */ public Array apply(final Array> lf) { - return lf.bind(f -> map(a -> f.f(a))); + return lf.bind(f -> map(f)); } /** @@ -535,7 +532,7 @@ public Array reverse() { x[a.length - 1 - i] = a[i]; } - return new Array(x); + return new Array<>(x); } /** @@ -550,7 +547,7 @@ public Array append(final Array aas) { arraycopy(a, 0, x, 0, a.length); arraycopy(aas.a, 0, x, a.length, aas.a.length); - return new Array(x); + return new Array<>(x); } /** @@ -559,7 +556,7 @@ public Array append(final Array aas) { * @return An empty array. */ public static Array empty() { - return new Array(new Object[0]); + return new Array<>(new Object[0]); } /** @@ -580,7 +577,7 @@ public static Array array(final A...as) { * @return A wrapped array. */ static Array mkArray(final Object[] a) { - return new Array(a); + return new Array<>(a); } /** @@ -590,7 +587,7 @@ static Array mkArray(final Object[] a) { * @return An array with the given single element. */ public static Array single(final A a) { - return new Array(new Object[]{a}); + return new Array<>(new Object[]{a}); } /** @@ -599,7 +596,7 @@ public static Array single(final A a) { * @return A function that wraps a given array. */ public static F> wrap() { - return as -> array(as); + return Array::array; } /** @@ -628,7 +625,7 @@ public static Array join(final Array> o) { * @return A function that joins a array of arrays using a bind operation. */ public static F>, Array> join() { - return as -> join(as); + return Array::join; } /** @@ -667,7 +664,7 @@ public boolean exists(final F f) { @Override public boolean equals(Object o) { - return Equal.equals0(Array.class, this, o, () -> Equal.arrayEqual(Equal.anyEqual())); + return Equal.equals0(Array.class, this, o, () -> Equal.arrayEqual(Equal.anyEqual())); } /** @@ -700,7 +697,7 @@ public static Array range(final int from, final int to) { if (from >= to) return empty(); else { - final Array a = new Array(new Integer[to - from]); + final Array a = new Array<>(new Integer[to - from]); for (int i = from; i < to; i++) a.set(i - from, i); @@ -720,7 +717,7 @@ public static Array range(final int from, final int to) { */ public Array zipWith(final Array bs, final F> f) { final int len = min(a.length, bs.length()); - final Array x = new Array(new Object[len]); + final Array x = new Array<>(new Object[len]); for (int i = 0; i < len; i++) { x.set(i, f.f(get(i)).f(bs.get(i))); @@ -768,44 +765,25 @@ public Array> zipIndex() { * * @return An immutable collection of this array. */ - @SuppressWarnings("unchecked") public Collection toCollection() { - return new AbstractCollection() { - public Iterator iterator() { - return new Iterator() { - private int i; - - public boolean hasNext() { - return i < a.length; - } - - public A next() { - if (i >= a.length) - throw new NoSuchElementException(); - else { - final A aa = (A) a[i]; - i++; - return aa; - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } + return asJavaList(); + } - public int size() { - return a.length; - } - }; + /** + * Projects an unmodifiable list view of this array. + * + * @return An unmodifiable list view of this array. + */ + @SuppressWarnings("unchecked") + public java.util.List asJavaList() { + return Collections.unmodifiableList(Arrays.asList((A[]) a)); } /** - * Returns a standard java.util.List projection of this array. + * Returns a java.util.ArrayList projection of this array. */ - java.util.List toJavaList() { - return new ArrayList(toCollection()); + public ArrayList toJavaList() { + return new ArrayList<>(asJavaList()); } /** @@ -833,7 +811,7 @@ public static Array iteratorArray(final Iterator i) { */ @SafeVarargs public static Array arrayArray(final A...as) { - return new Array(as); + return new Array<>(as); } /** @@ -842,17 +820,17 @@ public static Array arrayArray(final A...as) { * @param xs The array of pairs to transform. * @return An array of first components and an array of second components. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public static P2, Array> unzip(final Array> xs) { final int len = xs.length(); - final Array aa = new Array(new Object[len]); - final Array ab = new Array(new Object[len]); + final Array aa = new Array<>(new Object[len]); + final Array ab = new Array<>(new Object[len]); for (int i = len - 1; i >= 0; i--) { final P2 p = xs.get(i); aa.set(i, p._1()); ab.set(i, p._2()); } - return P.p(aa, ab); + return p(aa, ab); } /** @@ -1067,12 +1045,12 @@ public static T[] copyOf(final U[] a, final int len, final Class T[] copyOf(final T[] a, final int len) { return (T[]) copyOf(a, len, a.getClass()); } @@ -1082,8 +1060,8 @@ public static char[] copyOfRange(final char[] a, final int from, final int to) { if (len < 0) throw new IllegalArgumentException(from + " > " + to); final char[] copy = new char[len]; - System.arraycopy(a, from, copy, 0, - Math.min(a.length - from, len)); + arraycopy(a, from, copy, 0, + min(a.length - from, len)); return copy; } } diff --git a/java8/src/main/java/fj/data/Collectors.java b/core/src/main/java/fj/data/Collectors.java similarity index 88% rename from java8/src/main/java/fj/data/Collectors.java rename to core/src/main/java/fj/data/Collectors.java index 3f3d7e40..5ee9ead6 100644 --- a/java8/src/main/java/fj/data/Collectors.java +++ b/core/src/main/java/fj/data/Collectors.java @@ -21,7 +21,7 @@ public static Collector, Array> toArray() { List.Buffer::new, List.Buffer::snoc, (acc1, acc2) -> acc1.append(acc2.toList()), - (buf) -> Array.iterableArray(buf.toList()) + (buf) -> buf.toList().toArray() ); } @@ -30,7 +30,7 @@ public static Collector, Stream> toStream() { List.Buffer::new, List.Buffer::snoc, (acc1, acc2) -> acc1.append(acc2.toList()), - (buf) -> Stream.iterableStream(buf.toList()) + (buf) -> buf.toList().toStream() ); } } diff --git a/core/src/main/java/fj/data/Conversions.java b/core/src/main/java/fj/data/Conversions.java index 5405f70a..403ca6d1 100644 --- a/core/src/main/java/fj/data/Conversions.java +++ b/core/src/main/java/fj/data/Conversions.java @@ -36,7 +36,7 @@ private Conversions() { * @return A function that converts lists to arrays. */ public static F, Array> List_Array() { - return as -> as.toArray(); + return List::toArray; } /** @@ -45,7 +45,7 @@ public static F, Array> List_Array() { * @return A function that converts lists to streams. */ public static F, Stream> List_Stream() { - return as -> as.toStream(); + return List::toStream; } /** @@ -54,7 +54,7 @@ public static F, Stream> List_Stream() { * @return A function that converts lists to options. */ public static F, Option> List_Option() { - return as -> as.headOption(); + return List::headOption; } /** @@ -69,7 +69,7 @@ public static F, F, Either>> List_Either() { /** * A function that converts lists to strings. */ - public static final F, String> List_String = cs -> asString(cs); + public static final F, String> List_String = List::asString; /** * A function that converts lists to string buffers. @@ -91,7 +91,7 @@ public static F, F, Either>> List_Either() { * @return A function that converts arrays to lists. */ public static F, List> Array_List() { - return as -> as.toList(); + return Array::toList; } /** @@ -100,7 +100,7 @@ public static F, List> Array_List() { * @return A function that converts arrays to streams. */ public static F, Stream> Array_Stream() { - return as -> as.toStream(); + return Array::toStream; } /** @@ -109,7 +109,7 @@ public static F, Stream> Array_Stream() { * @return A function that converts arrays to options. */ public static F, Option> Array_Option() { - return as -> as.toOption(); + return Array::toOption; } /** @@ -125,8 +125,8 @@ public static F, F, Either>> Array_Either() { * A function that converts arrays to strings. */ public static final F, String> Array_String = cs -> { - final StringBuilder sb = new StringBuilder(); - cs.foreachDoEffect(c -> sb.append(c)); + final StringBuilder sb = new StringBuilder(cs.length()); + cs.foreachDoEffect(sb::append); return sb.toString(); }; @@ -134,8 +134,8 @@ public static F, F, Either>> Array_Either() { * A function that converts arrays to string buffers. */ public static final F, StringBuffer> Array_StringBuffer = cs -> { - final StringBuffer sb = new StringBuffer(); - cs.foreachDoEffect(c -> sb.append(c)); + final StringBuffer sb = new StringBuffer(cs.length()); + cs.foreachDoEffect(sb::append); return sb; }; @@ -143,8 +143,8 @@ public static F, F, Either>> Array_Either() { * A function that converts arrays to string builders. */ public static final F, StringBuilder> Array_StringBuilder = cs -> { - final StringBuilder sb = new StringBuilder(); - cs.foreachDoEffect((Character c) -> sb.append(c)); + final StringBuilder sb = new StringBuilder(cs.length()); + cs.foreachDoEffect(sb::append); return sb; }; @@ -158,7 +158,7 @@ public static F, F, Either>> Array_Either() { * @return A function that converts streams to lists. */ public static F, List> Stream_List() { - return as -> as.toList(); + return Stream::toList; } /** @@ -167,7 +167,7 @@ public static F, List> Stream_List() { * @return A function that converts streams to arrays. */ public static F, Array> Stream_Array() { - return as -> as.toArray(); + return Stream::toArray; } /** @@ -176,7 +176,7 @@ public static F, Array> Stream_Array() { * @return A function that converts streams to options. */ public static F, Option> Stream_Option() { - return as -> as.toOption(); + return Stream::toOption; } /** @@ -193,7 +193,7 @@ public static F, F, Either>> Stream_Either() { */ public static final F, String> Stream_String = cs -> { final StringBuilder sb = new StringBuilder(); - cs.foreachDoEffect((Character c) -> sb.append(c)); + cs.foreachDoEffect(sb::append); return sb.toString(); }; @@ -202,7 +202,7 @@ public static F, F, Either>> Stream_Either() { */ public static final F, StringBuffer> Stream_StringBuffer = cs -> { final StringBuffer sb = new StringBuffer(); - cs.foreachDoEffect((Character c) -> sb.append(c)); + cs.foreachDoEffect(sb::append); return sb; }; @@ -211,7 +211,7 @@ public static F, F, Either>> Stream_Either() { */ public static final F, StringBuilder> Stream_StringBuilder = cs -> { final StringBuilder sb = new StringBuilder(); - cs.foreachDoEffect((Character c) -> sb.append(c)); + cs.foreachDoEffect(sb::append); return sb; }; @@ -225,7 +225,7 @@ public static F, F, Either>> Stream_Either() { * @return A function that converts options to lists. */ public static F, List> Option_List() { - return o -> o.toList(); + return Option::toList; } /** @@ -234,7 +234,7 @@ public static F, List> Option_List() { * @return A function that converts options to arrays. */ public static F, Array> Option_Array() { - return o -> o.toArray(); + return Option::toArray; } /** @@ -243,7 +243,7 @@ public static F, Array> Option_Array() { * @return A function that converts options to streams. */ public static F, Stream> Option_Stream() { - return o -> o.toStream(); + return Option::toStream; } /** @@ -275,7 +275,7 @@ public static F, F, Either>> Option_Either() { // BEGIN Effect public static F> Effect0_P1() { - return e -> Effect0_P1(e); + return Conversions::Effect0_P1; } public static P1 Effect0_P1(Effect0 e) { @@ -287,18 +287,18 @@ public static F Effect1_F(Effect1 e) { } public static F, F> Effect1_F() { - return e -> Effect1_F(e); + return Conversions::Effect1_F; } public static IO Effect_IO(Effect0 e) { return () ->{ e.f(); - return Unit.unit(); + return unit(); }; } public static F> Effect_IO() { - return e -> Effect_IO(e); + return Conversions::Effect_IO; } public static SafeIO Effect_SafeIO(Effect0 e) { @@ -309,7 +309,7 @@ public static SafeIO Effect_SafeIO(Effect0 e) { } public static F> Effect_SafeIO() { - return e -> Effect_SafeIO(e); + return Conversions::Effect_SafeIO; } // END Effect @@ -451,7 +451,7 @@ public static SafeIO F_SafeIO(F f) { } public static F, SafeIO> F_SafeIO() { - return f -> F_SafeIO(f); + return Conversions::F_SafeIO; } // END F @@ -461,7 +461,7 @@ public static F, SafeIO> F_SafeIO() { /** * A function that converts strings to lists. */ - public static final F> String_List = s -> fromString(s); + public static final F> String_List = List::fromString; /** * A function that converts strings to arrays. @@ -490,12 +490,12 @@ public static F, F>> String_Either() { /** * A function that converts strings to string buffers. */ - public static final F String_StringBuffer = s -> new StringBuffer(s); + public static final F String_StringBuffer = StringBuffer::new; /** * A function that converts strings to string builders. */ - public static final F String_StringBuilder = s -> new StringBuilder(s); + public static final F String_StringBuilder = StringBuilder::new; // END String -> @@ -533,12 +533,12 @@ public static F, F>> StringBuffer_E /** * A function that converts string buffers to strings. */ - public static final F StringBuffer_String = s -> s.toString(); + public static final F StringBuffer_String = StringBuffer::toString; /** * A function that converts string buffers to string builders. */ - public static final F StringBuffer_StringBuilder = s -> new StringBuilder(s); + public static final F StringBuffer_StringBuilder = StringBuilder::new; // END StringBuffer -> @@ -576,12 +576,12 @@ public static F, F>> StringBuilder /** * A function that converts string builders to strings. */ - public static final F StringBuilder_String = s -> s.toString(); + public static final F StringBuilder_String = StringBuilder::toString; /** * A function that converts string builders to string buffers. */ - public static final F StringBuilder_StringBuffer = s -> new StringBuffer(s); + public static final F StringBuilder_StringBuffer = StringBuffer::new; // END StringBuilder -> @@ -593,15 +593,15 @@ public static SafeIO> Try_SafeIO(Tr } public static F, SafeIO>> Try_SafeIO() { - return t -> Try_SafeIO(t); + return Conversions::Try_SafeIO; } public static IO Try_IO(Try0 t) { - return () -> t.f(); + return t::f; } public static F, IO> Try_IO() { - return t -> Try_IO(t); + return Conversions::Try_IO; } public static F> Try_F(Try1 t) { @@ -609,14 +609,14 @@ public static F> Try_F(Try1 F, F>> Try_F() { - return t -> Try_F(t); + return Conversions::Try_F; } // END Try // BEGIN TryEffect - static public P1> TryEffect_P(final TryEffect0 t) { + public static P1> TryEffect_P(final TryEffect0 t) { return TryEffect.f(t); } diff --git a/core/src/main/java/fj/data/DList.java b/core/src/main/java/fj/data/DList.java index 2c7856de..597b6dda 100644 --- a/core/src/main/java/fj/data/DList.java +++ b/core/src/main/java/fj/data/DList.java @@ -13,7 +13,7 @@ * * @version %build.number% */ -public class DList { +public final class DList { private final F,Trampoline>> appendFn; private DList(final F,Trampoline>> appendFn) { @@ -69,13 +69,13 @@ public static DList arrayDList(final A...as) { * @return the final List */ public List run() { - return appendFn.f(List.nil()).run(); + return appendFn.f(List.nil()).run(); } /** * Converts the DList to a standard java.util.List. */ - public java.util.List toJavaList() { + public java.util.List toJavaList() { return run().toJavaList(); } @@ -85,7 +85,7 @@ public java.util.List toJavaList() { * @return a empty DList. */ public static DList nil() { - return new DList<>(Trampoline.>pure()); + return new DList<>(Trampoline.pure()); } /** @@ -104,7 +104,7 @@ public static DList single(A a) { * @return the new DList. */ public DList cons(A a) { - return DList.single(a).append(this); + return single(a).append(this); } /** @@ -113,7 +113,7 @@ public DList cons(A a) { * @return the new DList. */ public DList snoc(A a) { - return this.append(DList.single(a)); + return this.append(single(a)); } /** @@ -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/Either.java b/core/src/main/java/fj/data/Either.java index 8581664f..e710c1d6 100644 --- a/core/src/main/java/fj/data/Either.java +++ b/core/src/main/java/fj/data/Either.java @@ -1,24 +1,18 @@ package fj.data; -import static fj.Bottom.error; - import fj.*; - -import static fj.Function.identity; -import static fj.P.p; - import fj.function.Effect1; +import java.util.*; + +import static fj.Bottom.error; +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.list; -import static fj.data.List.single; -import static fj.data.List.cons_; +import static fj.data.List.*; import static fj.data.Option.some; -import java.util.Collection; -import java.util.Iterator; - /** * The Either type represents a value of one of two possible types (a disjoint union). * The data constructors; Left and Right represent the two possible @@ -39,7 +33,7 @@ private Either() { * @return A left projection of this either. */ public final LeftProjection left() { - return new LeftProjection(this); + return new LeftProjection<>(this); } /** @@ -48,7 +42,7 @@ public final LeftProjection left() { * @return A right projection of this either. */ public final RightProjection right() { - return new RightProjection(this); + return new RightProjection<>(this); } /** @@ -72,11 +66,7 @@ public final RightProjection right() { * @param right The function to call if this is right. * @return The reduced value. */ - public final X either(final F left, final F right) { - return isLeft() ? - left.f(left().value()) : - right.f(right().value()); - } + public abstract X either(final F left, final F right); /** * Map the given functions across the appropriate side. @@ -86,18 +76,16 @@ public final X either(final F left, final F right) { * @return A new either value after mapping with the appropriate function applied. */ public final Either bimap(final F left, final F right) { - return isLeft() ? - left(left.f(left().value())) : - right(right.f(right().value())); + return either(compose(left_(), left), compose(right_(), right)); } @Override - public boolean equals(Object other) { - return Equal.equals0(Either.class, this, other, () -> Equal.eitherEqual(Equal.anyEqual(), Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(Either.class, this, other, () -> Equal.eitherEqual(Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.eitherHash(Hash.anyHash(), Hash.anyHash()).hash(this); } @@ -107,7 +95,7 @@ public int hashCode() { * @return The value of this either swapped to the opposing side. */ public final Either swap() { - return isLeft() ? new Right(((Left) this).a) : new Left(((Right) this).b); + return either(right_(), left_()); } private static final class Left extends Either { @@ -124,6 +112,11 @@ public boolean isLeft() { public boolean isRight() { return false; } + + @Override + public X either(F left, F right) { + return left.f(a); + } } private static final class Right extends Either { @@ -140,12 +133,17 @@ public boolean isLeft() { public boolean isRight() { return true; } + + @Override + public X either(F left, F right) { + return right.f(b); + } } /** * A left projection of an either value. */ - public final class LeftProjection implements Iterable { + public static final class LeftProjection implements Iterable { private final Either e; private LeftProjection(final Either e) { @@ -210,7 +208,7 @@ public A value() { * @return The value of this projection or the given argument. */ public A orValue(final F0 a) { - return isLeft() ? value() : a.f(); + return e.isLeft() ? value() : a.f(); } /** @@ -220,7 +218,7 @@ public A orValue(final F0 a) { * @return The value of this projection or the given argument. */ public A orValue(final A a) { - return isLeft() ? value() : a; + return e.isLeft() ? value() : a; } /** @@ -232,7 +230,7 @@ public A orValue(final A a) { * value. */ public A on(final F f) { - return isLeft() ? value() : f.f(e.right().value()); + return e.isLeft() ? value() : f.f(e.right().value()); } /** @@ -242,7 +240,7 @@ public A on(final F f) { * @return The unit value. */ public Unit foreach(final F f) { - if (isLeft()) + if (e.isLeft()) f.f(value()); return unit(); @@ -254,7 +252,7 @@ public Unit foreach(final F f) { * @param f The side-effect to execute. */ public void foreachDoEffect(final Effect1 f) { - if (isLeft()) + if (e.isLeft()) f.f(value()); } @@ -265,7 +263,7 @@ public void foreachDoEffect(final Effect1 f) { * @return A new either value after mapping. */ public Either map(final F f) { - return isLeft() ? new Left(f.f(value())) : new Right(e.right().value()); + return e.isLeft() ? new Left<>(f.f(value())) : new Right<>(e.right().value()); } /** @@ -275,7 +273,7 @@ public Either map(final F f) { * @return A new either value after binding. */ public Either bind(final F> f) { - return isLeft() ? f.f(value()) : new Right(e.right().value()); + return e.isLeft() ? f.f(value()) : new Right<>(e.right().value()); } /** @@ -285,7 +283,7 @@ public Either bind(final F> f) { * @return An either after binding through this projection. */ public Either sequence(final Either e) { - return bind(Function.>constant(e)); + return bind(Function.constant(e)); } /** @@ -295,9 +293,9 @@ public Either sequence(final Either e) { * @return An either after traversing through this projection. */ public List> traverseList(final F> f) { - return isLeft() ? - f.f(value()).map(x -> Either.left(x)) : - list(Either.right(e.right().value())); + return e.isLeft() ? + f.f(value()).map(Either::left) : + list(right(e.right().value())); } /** @@ -307,11 +305,24 @@ public List> traverseList(final F> f) { * @return An either after traversing through this projection. */ public IO> traverseIO(final F> f) { - return isRight() ? - IOFunctions.map(f.f(value()), x -> Either.left(x)) : - IOFunctions.unit(Either.right(e.right().value())); + return e.isLeft() ? + IOFunctions.map(f.f(value()), Either::left) : + 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. @@ -321,11 +332,11 @@ public IO> traverseIO(final F> f) { * p does not hold for the value, otherwise, returns a right in Some. */ public Option> filter(final F f) { - return isLeft() ? + return e.isLeft() ? f.f(value()) ? - Option.>some(new Left<>(value())) : - Option.>none() : - Option.>none(); + some(new Left<>(value())) : + Option.none() : + Option.none(); } /** @@ -335,7 +346,7 @@ public Option> filter(final F f) { * @return The result of function application within either. */ public Either apply(final Either, B> e) { - return e.left().bind(f -> map(f)); + return e.left().bind(this::map); } /** @@ -347,7 +358,7 @@ public Either apply(final Either, B> e) { * function to the value. */ public boolean forall(final F f) { - return isRight() || f.f(value()); + return e.isRight() || f.f(value()); } /** @@ -359,7 +370,7 @@ public boolean forall(final F f) { * function to the value. */ public boolean exists(final F f) { - return isLeft() && f.f(value()); + return e.isLeft() && f.f(value()); } /** @@ -368,7 +379,7 @@ public boolean exists(final F f) { * @return A single element list if this projection has a value, otherwise an empty list. */ public List toList() { - return isLeft() ? single(value()) : List.nil(); + return e.isLeft() ? single(value()) : List.nil(); } /** @@ -379,7 +390,7 @@ public List toList() { * None. */ public Option toOption() { - return isLeft() ? some(value()) : Option.none(); + return e.isLeft() ? some(value()) : Option.none(); } /** @@ -388,7 +399,7 @@ public Option toOption() { * @return A single element array if this projection has a value, otherwise an empty array. */ public Array toArray() { - if (isLeft()) { + if (e.isLeft()) { final Object[] a = new Object[1]; a[0] = value(); return mkArray(a); @@ -402,7 +413,7 @@ public Array toArray() { * @return A single element stream if this projection has a value, otherwise an empty stream. */ public Stream toStream() { - return isLeft() ? Stream.single(value()) : Stream.nil(); + return e.isLeft() ? Stream.single(value()) : Stream.nil(); } /** @@ -414,23 +425,23 @@ public Collection toCollection() { return toList().toCollection(); } - public Option> traverseOption(F> f) { - return isLeft() ? - f.f(value()).map(x -> Either.left(x)) : - Option.some(Either.right(e.right().value())); - } + public Option> traverseOption(F> f) { + return e.isLeft() ? + f.f(value()).map(left_()) : + some(right(e.right().value())); + } - public Stream> traverseStream(F> f) { - return isLeft() ? - f.f(value()).map(c -> Either.left(c)) : - Stream.single(Either.right(e.right().value())); - } + public Stream> traverseStream(F> f) { + return e.isLeft() ? + f.f(value()).map(left_()) : + Stream.single(right(e.right().value())); + } } /** * A right projection of an either value. */ - public final class RightProjection implements Iterable { + public static final class RightProjection implements Iterable { private final Either e; private RightProjection(final Either e) { @@ -455,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. * @@ -478,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. * @@ -485,7 +516,7 @@ public B value() { * @return The value of this projection or the given argument. */ public B orValue(final F0 b) { - return isRight() ? value() : b.f(); + return e.isRight() ? value() : b.f(); } /** @@ -497,7 +528,7 @@ public B orValue(final F0 b) { * value. */ public B on(final F f) { - return isRight() ? value() : f.f(e.left().value()); + return e.isRight() ? value() : f.f(e.left().value()); } /** @@ -507,7 +538,7 @@ public B on(final F f) { * @return The unit value. */ public Unit foreach(final F f) { - if (isRight()) + if (e.isRight()) f.f(value()); return unit(); @@ -519,7 +550,7 @@ public Unit foreach(final F f) { * @param f The side-effect to execute. */ public void foreachDoEffect(final Effect1 f) { - if (isRight()) + if (e.isRight()) f.f(value()); } @@ -530,7 +561,7 @@ public void foreachDoEffect(final Effect1 f) { * @return A new either value after mapping. */ public Either map(final F f) { - return isRight() ? new Right<>(f.f(value())) : new Left<>(e.left().value()); + return e.isRight() ? new Right<>(f.f(value())) : new Left<>(e.left().value()); } /** @@ -540,10 +571,9 @@ public Either map(final F f) { * @return A new either value after binding. */ public Either bind(final F> f) { - return isRight() ? f.f(value()) : new Left<>(e.left().value()); + return e.isRight() ? f.f(value()) : new Left<>(e.left().value()); } - /** * Anonymous bind through this projection. * @@ -551,19 +581,20 @@ public Either bind(final F> f) { * @return An either after binding through this projection. */ public Either sequence(final Either e) { - return bind(Function.>constant(e)); + return bind(Function.constant(e)); } + /** - * Traverse with function that produces List (non-determinism). - * - * @param f the function to traverse with - * @return An either after traversing through this projection. - */ - public List> traverseList(final F> f) { - return isRight() ? - f.f(value()).map(x -> Either.right(x)) : - list(Either.left(e.left().value())); - } + * Traverse with function that produces List (non-determinism). + * + * @param f the function to traverse with + * @return An either after traversing through this projection. + */ + public List> traverseList(final F> f) { + return e.isRight() ? + f.f(value()).map(right_()) : + list(left(e.left().value())); + } /** * Traverse with a function that has IO effect @@ -572,21 +603,35 @@ public List> traverseList(final F> f) { * @return An either after traversing through this projection. */ public IO> traverseIO(final F> f) { - return isRight() ? - IOFunctions.map(f.f(value()), x -> Either.right(x)) : - IOFunctions.lazy(() -> Either.left(e.left().value())); + return e.isRight() ? + IOFunctions.map(f.f(value()), right_()) : + 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 isRight() ? - f.f(value()).map(x -> Either.right(x)) : - P.p(Either.left(e.left().value())); + 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 isRight() ? - f.f(value()).map(x -> Either.right(x)) : - Option.some(Either.left(e.left().value())); + return e.isRight() ? + f.f(value()).map(right_()) : + some(left(e.left().value())); } /** @@ -598,11 +643,11 @@ public Option> traverseOption(final F> f) { * p does not hold for the value, otherwise, returns a left in Some. */ public Option> filter(final F f) { - return isRight() ? + return e.isRight() ? f.f(value()) ? - Option.>some(new Right(value())) : - Option.>none() : - Option.>none(); + some(new Right(value())) : + Option.none() : + Option.none(); } /** @@ -612,7 +657,7 @@ public Option> filter(final F f) { * @return The result of function application within either. */ public Either apply(final Either> e) { - return e.right().bind(f -> map(f)); + return e.right().bind(this::map); } /** @@ -624,7 +669,7 @@ public Either apply(final Either> e) { * function to the value. */ public boolean forall(final F f) { - return isLeft() || f.f(value()); + return e.isLeft() || f.f(value()); } /** @@ -636,7 +681,7 @@ public boolean forall(final F f) { * function to the value. */ public boolean exists(final F f) { - return isRight() && f.f(value()); + return e.isRight() && f.f(value()); } /** @@ -645,7 +690,7 @@ public boolean exists(final F f) { * @return A single element list if this projection has a value, otherwise an empty list. */ public List toList() { - return isRight() ? single(value()) : List.nil(); + return e.isRight() ? single(value()) : List.nil(); } /** @@ -656,7 +701,7 @@ public List toList() { * None. */ public Option toOption() { - return isRight() ? some(value()) : Option.none(); + return e.isRight() ? some(value()) : Option.none(); } /** @@ -665,7 +710,7 @@ public Option toOption() { * @return A single element array if this projection has a value, otherwise an empty array. */ public Array toArray() { - if (isRight()) { + if (e.isRight()) { final Object[] a = new Object[1]; a[0] = value(); return mkArray(a); @@ -679,7 +724,7 @@ public Array toArray() { * @return A single element stream if this projection has a value, otherwise an empty stream. */ public Stream toStream() { - return isRight() ? Stream.single(value()) : Stream.nil(); + return e.isRight() ? Stream.single(value()) : Stream.nil(); } /** @@ -692,9 +737,9 @@ public Collection toCollection() { } public Stream> traverseStream(F> f) { - return isRight() ? - f.f(value()).map(x -> Either.right(x)) : - Stream.>single(Either.left(e.left().value())); + return e.isRight() ? + f.f(value()).map(Either::right) : + Stream.single(left(e.left().value())); } } @@ -706,7 +751,7 @@ public Stream> traverseStream(F> f) { * @return A left value of either. */ public static Either left(final A a) { - return new Left(a); + return new Left<>(a); } /** @@ -715,7 +760,7 @@ public static Either left(final A a) { * @return A function that constructs a left value of either. */ public static F> left_() { - return a -> left(a); + return Either::left; } /** @@ -724,7 +769,7 @@ public static F> left_() { * @return A function that constructs a right value of either. */ public static F> right_() { - return b -> right(b); + return Either::right; } /** @@ -734,10 +779,37 @@ public static F> right_() { * @return A right value of either. */ public static Either right(final B b) { - return new Right(b); + return new Right<>(b); + } + + /** + * First class catamorphism for either. Folds over this either breaking into left or right. + * + * @param left The function to call if this is left. + * @param right The function to call if this is right. + * @return The reducing function. + */ + public static F, X> either_(final F left, final F right) { + return e -> e.either(left, right); } /** + * Map the given function across this either's left projection. + * + * @param f the given function + * @param 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_() { @@ -745,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_() { @@ -758,8 +846,7 @@ public static F, F, Either>> rightMap_() { * @return An either after joining. */ public static Either joinLeft(final Either, B> e) { - final F, Either> id = identity(); - return e.left().bind(id); + return e.left().bind(identity()); } /** @@ -769,8 +856,7 @@ public static Either joinLeft(final Either, B> e) { * @return An either after joining. */ public static Either joinRight(final Either> e) { - final F, Either> id = identity(); - return e.right().bind(id); + return e.right().bind(identity()); } /** @@ -778,10 +864,11 @@ 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() ? - Either., X>left(List.nil()) : + left(List.nil()) : a.head().left().bind(aa -> sequenceLeft(a.tail()).left().map(cons_(aa))); } @@ -790,10 +877,12 @@ 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() ? - Either.>right(List.nil()) : + right(List.nil()) : a.head().right().bind(bb -> sequenceRight(a.tail()).right().map(cons_(bb))); } @@ -802,17 +891,17 @@ public static Either> sequenceRight(final List> a * * @return traversed value */ - public List> traverseListRight(final F> f) { - return right().traverseList(f); + public final List> traverseListRight(final F> f) { + return right().traverseList(f); } /** - * Traversable instance of LeftProjection of Either for List. - * - * @return traversed value - */ - public List> traverseListLeft(final F> f) { - return left().traverseList(f); + * Traversable instance of LeftProjection of Either for List. + * + * @return traversed value + */ + public final List> traverseListLeft(final F> f) { + return left().traverseList(f); } /** @@ -820,8 +909,8 @@ public List> traverseListLeft(final F> f) { * * @return traversed value */ - public IO> traverseIORight(final F> f) { - return right().traverseIO(f); + public final IO> traverseIORight(final F> f) { + return right().traverseIO(f); } /** @@ -829,8 +918,8 @@ public IO> traverseIORight(final F> f) { * * @return traversed value */ - public IO> traverseIOLeft(final F> f) { - return left().traverseIO(f); + public final IO> traverseIOLeft(final F> f) { + return left().traverseIO(f); } /** @@ -838,8 +927,8 @@ public IO> traverseIOLeft(final F> f) { * * @return traversed value */ - public Option> traverseOptionRight(final F> f) { - return right().traverseOption(f); + public final Option> traverseOptionRight(final F> f) { + return right().traverseOption(f); } /** @@ -847,8 +936,8 @@ public Option> traverseOptionRight(final F> f) { * * @return traversed value */ - public Option> traverseOptionLeft(final F> f) { - return left().traverseOption(f); + public final Option> traverseOptionLeft(final F> f) { + return left().traverseOption(f); } /** @@ -856,8 +945,8 @@ public Option> traverseOptionLeft(final F> f) { * * @return traversed value */ - public Stream> traverseStreamRight(final F> f) { - return right().traverseStream(f); + public final Stream> traverseStreamRight(final F> f) { + return right().traverseStream(f); } /** @@ -865,12 +954,10 @@ public Stream> traverseStreamRight(final F> f) { * * @return traversed value */ - public Stream> traverseStreamLeft(final F> f) { - return left().traverseStream(f); + public final Stream> traverseStreamLeft(final F> f) { + return left().traverseStream(f); } - - /** * Takes an Either to its contained value within left or right. * @@ -878,11 +965,11 @@ public Stream> traverseStreamLeft(final F> f) { * @return An Either to its contained value within left or right. */ public static A reduce(final Either e) { - return e.isLeft() ? e.left().value() : e.right().value(); + return e.either(identity(), identity()); } /** - * 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. @@ -890,7 +977,7 @@ public static A reduce(final Either e) { * @return A constructed either based on the given condition. */ public static Either iif(final boolean c, final F0 right, final F0 left) { - return c ? new Right(right.f()) : new Left(left.f()); + return c ? new Right<>(right.f()) : new Left<>(left.f()); } /** @@ -900,7 +987,7 @@ public static Either iif(final boolean c, final F0 right, final * @return All the left values in the given list. */ public static List lefts(final List> es) { - return es.foldRight(e -> as -> e.isLeft() ? as.cons(e.left().value()) : as, List.nil()); + return es.foldRight(e -> as -> e.isLeft() ? as.cons(e.left().value()) : as, List.nil()); } /** @@ -910,10 +997,10 @@ public static List lefts(final List> es) { * @return All the right values in the given list. */ public static List rights(final List> es) { - return es.foldRight(e -> bs -> e.isRight() ? bs.cons(e.right().value()) : bs, List.nil()); + return es.foldRight(e -> bs -> e.isRight() ? bs.cons(e.right().value()) : bs, List.nil()); } - public String toString() { + public final String toString() { return Show.eitherShow(Show.anyShow(), Show.anyShow()).showS(this); } 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..9b545f7e --- /dev/null +++ b/core/src/main/java/fj/data/Either3.java @@ -0,0 +1,580 @@ +package fj.data; + +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; + +public abstract class Either3 { + + private Either3() {} + + private static final class Left extends Either3 { + private final A a; + + Left(A a) { + this.a = a; + } + + @Override + public D either(F fa, F fb, F fc) { + return fa.f(a); + } + + } + + private static final class Middle extends Either3 { + private final B b; + + Middle(B b) { + this.b = b; + } + + @Override + public D either(F fa, F fb, F fc) { + return fb.f(b); + } + } + + private static final class Right extends Either3 { + private final C c; + Right(C c) { + this.c = c; + } + + @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 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 { + private final Either3 e; + + 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 { + private final Either3 e; + + 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) { + return new Left<>(a); + } + + public static F> left_() { + return Either3::left; + } + + public static Either3 middle(B b) { + return new Middle<>(b); + } + + public static Either3 right(C c) { + return new Right<>(c); + } + + public boolean isLeft() { + return either(a -> true, b -> false, c -> false); + } + + public boolean isMiddle() { + return either(a -> false, b -> true, c -> false); + } + + public boolean isRight() { + return either(a -> false, b -> false, c -> true); + } + + 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 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)); + } + + 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 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()); + } + + 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/main/java/fj/data/Enumerator.java b/core/src/main/java/fj/data/Enumerator.java index fe57a643..2fa96a6f 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.F2; +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; @@ -18,18 +14,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% @@ -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 -> order.isLessThanOrEqualTo(item, bounded.max())); + } + /** * Create a new enumerator with the given minimum value. * @@ -220,7 +228,7 @@ public Enumerator setMax(final Option max) { public static Enumerator enumerator(final F> successor, final F> predecessor, final Option max, final Option min, final Ord order, final F>> plus) { - return new Enumerator(successor, predecessor, max, min, order, plus); + return new Enumerator<>(successor, predecessor, max, min, order, plus); } /** @@ -236,30 +244,30 @@ public static Enumerator enumerator(final F> successor, fina */ public static Enumerator enumerator(final F> successor, final F> predecessor, final Option max, final Option min, final Ord order) { - return new Enumerator(successor, predecessor, max, min, order, curry((a, l) -> { - if (l == 0L) - return some(a); - else if (l < 0L) { - A aa = a; - for (long x = l; x < 0; x++) { - final Option s = predecessor.f(aa); - if (s.isNone()) - return none(); - else - aa = s.some(); - } - return some(aa); - } else { - A aa = a; - for (long x = l; x > 0; x--) { - final Option s = successor.f(aa); - if (s.isNone()) - return none(); - else - aa = s.some(); - } - return some(aa); + return new Enumerator<>(successor, predecessor, max, min, order, curry((a, l) -> { + if (l == 0L) + return some(a); + else if (l < 0L) { + A aa = a; + for (long x = l; x < 0; x++) { + final Option s = predecessor.f(aa); + if (s.isNone()) + return none(); + else + aa = s.some(); + } + return some(aa); + } else { + A aa = a; + for (long x = l; x > 0; x--) { + final Option s = successor.f(aa); + if (s.isNone()) + return none(); + else + aa = s.some(); } + return some(aa); + } })); } @@ -267,8 +275,8 @@ else if (l < 0L) { * An enumerator for boolean. */ public static final Enumerator booleanEnumerator = enumerator( - b -> b ? Option.none() : some(true), - b -> b ? some(false) : Option.none(), + b -> b ? Option.none() : some(true), + b -> b ? some(false) : Option.none(), some(true), some(false), booleanOrd ); @@ -276,8 +284,8 @@ else if (l < 0L) { * An enumerator for byte. */ public static final Enumerator byteEnumerator = enumerator( - b -> b == Byte.MAX_VALUE ? Option.none() : some((byte) (b + 1)), - b -> b == Byte.MIN_VALUE ? Option.none() : some((byte) (b - 1)), + b -> b == Byte.MAX_VALUE ? Option.none() : some((byte) (b + 1)), + b -> b == Byte.MIN_VALUE ? Option.none() : some((byte) (b - 1)), some(Byte.MAX_VALUE), some(Byte.MIN_VALUE), byteOrd ); @@ -285,8 +293,8 @@ else if (l < 0L) { * An enumerator for char. */ public static final Enumerator charEnumerator = enumerator( - c -> c == Character.MAX_VALUE ? Option.none() : some((char) (c + 1)), - c -> c == Character.MIN_VALUE ? Option.none() : some((char) (c - 1)), + c -> c == Character.MAX_VALUE ? Option.none() : some((char) (c + 1)), + c -> c == Character.MIN_VALUE ? Option.none() : some((char) (c - 1)), some(Character.MAX_VALUE), some(Character.MIN_VALUE), charOrd ); @@ -294,8 +302,8 @@ else if (l < 0L) { * An enumerator for double. */ public static final Enumerator doubleEnumerator = enumerator( - d -> d == Double.MAX_VALUE ? Option.none() : some(d + 1D), - d -> d == Double.MIN_VALUE ? Option.none() : some(d - 1D), + d -> d == Double.MAX_VALUE ? Option.none() : some(d + 1D), + d -> d == Double.MIN_VALUE ? Option.none() : some(d - 1D), some(Double.MAX_VALUE), some(Double.MIN_VALUE), doubleOrd ); @@ -303,8 +311,8 @@ else if (l < 0L) { * An enumerator for float. */ public static final Enumerator floatEnumerator = enumerator( - f -> f == Float.MAX_VALUE ? Option.none() : some(f + 1F), - f -> f == Float.MIN_VALUE ? Option.none() : some(f - 1F), + f -> f == Float.MAX_VALUE ? Option.none() : some(f + 1F), + f -> f == Float.MIN_VALUE ? Option.none() : some(f - 1F), some(Float.MAX_VALUE), some(Float.MIN_VALUE), floatOrd ); @@ -312,8 +320,8 @@ else if (l < 0L) { * An enumerator for int. */ public static final Enumerator intEnumerator = enumerator( - i -> i == Integer.MAX_VALUE ? Option.none() : some(i + 1), - i -> i == Integer.MIN_VALUE ? Option.none() : some(i - 1), + i -> i == Integer.MAX_VALUE ? Option.none() : some(i + 1), + i -> i == Integer.MIN_VALUE ? Option.none() : some(i - 1), some(Integer.MAX_VALUE), some(Integer.MIN_VALUE), intOrd ); @@ -323,7 +331,7 @@ else if (l < 0L) { public static final Enumerator bigintEnumerator = enumerator( i -> some(i.add(BigInteger.ONE)), i -> some(i.subtract(BigInteger.ONE)), - Option.none(), Option.none(), bigintOrd, + Option.none(), Option.none(), bigintOrd, curry((i, l) -> some(i.add(BigInteger.valueOf(l)))) ); @@ -333,7 +341,7 @@ else if (l < 0L) { public static final Enumerator bigdecimalEnumerator = enumerator( i -> some(i.add(BigDecimal.ONE)), i -> some(i.subtract(BigDecimal.ONE)), - Option.none(), Option.none(), bigdecimalOrd, + Option.none(), Option.none(), bigdecimalOrd, curry((d, l) -> some(d.add(BigDecimal.valueOf(l)))) ); @@ -341,8 +349,8 @@ else if (l < 0L) { * An enumerator for long. */ public static final Enumerator longEnumerator = enumerator( - i -> i == Long.MAX_VALUE ? Option.none() : some(i + 1L), - i -> i == Long.MIN_VALUE ? Option.none() : some(i - 1L), + i -> i == Long.MAX_VALUE ? Option.none() : some(i + 1L), + i -> i == Long.MIN_VALUE ? Option.none() : some(i - 1L), some(Long.MAX_VALUE), some(Long.MIN_VALUE), longOrd ); @@ -350,8 +358,8 @@ else if (l < 0L) { * An enumerator for short. */ public static final Enumerator shortEnumerator = enumerator( - i -> i == Short.MAX_VALUE ? Option.none() : some((short) (i + 1)), - i -> i == Short.MIN_VALUE ? Option.none() : some((short) (i - 1)), + i -> i == Short.MAX_VALUE ? Option.none() : some((short) (i + 1)), + i -> i == Short.MIN_VALUE ? Option.none() : some((short) (i - 1)), some(Short.MAX_VALUE), some(Short.MIN_VALUE), shortOrd ); @@ -359,8 +367,8 @@ else if (l < 0L) { * An enumerator for Ordering. */ public static final Enumerator orderingEnumerator = enumerator( - o -> o == LT ? some(EQ) : o == EQ ? some(GT) : Option.none(), - o -> o == GT ? some(EQ) : o == EQ ? some(LT) : Option.none(), + o -> o == LT ? some(EQ) : o == EQ ? some(GT) : Option.none(), + o -> o == GT ? some(EQ) : o == EQ ? some(LT) : Option.none(), some(GT), some(LT), orderingOrd ); @@ -368,12 +376,12 @@ else if (l < 0L) { * An enumerator for Natural */ public static final Enumerator naturalEnumerator = enumerator( - n -> Option.some(n.succ()), - n -> n.pred(), - Option.none(), some(Natural.ZERO), naturalOrd, + n -> some(n.succ()), + Natural::pred, + Option.none(), some(Natural.ZERO), naturalOrd, curry((n, l) -> some(n).apply( - Natural.natural(l).map(Function.curry((n1, n2) -> n1.add(n2))) + Natural.natural(l).map(curry((n1, n2) -> n1.add(n2))) ) ) ); 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..5d09f913 --- /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(() -> 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(() -> 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 Trampoline.suspend(() -> memo._1().asTrampoline().trampoline()); + } + } +} diff --git a/core/src/main/java/fj/data/HashMap.java b/core/src/main/java/fj/data/HashMap.java index 7cd9c8dd..455c529c 100755 --- a/core/src/main/java/fj/data/HashMap.java +++ b/core/src/main/java/fj/data/HashMap.java @@ -8,6 +8,7 @@ import java.util.Map; import static fj.P.p; +import static fj.data.List.iterableList; import static fj.data.Option.fromNull; /** @@ -17,24 +18,16 @@ * @see java.util.HashMap */ public final class HashMap implements Iterable { - private final class Key { - private final K k; - private final Equal e; - private final Hash h; + private final class Key { + final K k; - Key(final K k, final Equal e, final Hash h) { + Key(final K k) { this.k = k; - this.e = e; - this.h = h; } - K k() { - return k; - } - - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public boolean equals(final Object o) { - return o instanceof Key && e.eq(k, (K) ((Key) o).k()); + return o instanceof HashMap.Key && e.eq(k, ((Key) o).k); } public int hashCode() { @@ -51,7 +44,7 @@ public Iterator iterator() { return keys().iterator(); } - private final java.util.HashMap, V> m; + private final java.util.HashMap m; private final Equal e; private final Hash h; @@ -63,15 +56,15 @@ public Iterator iterator() { * @param h The hashing strategy. */ public HashMap(final Equal e, final Hash h) { - m = new java.util.HashMap, V>(); + m = new java.util.HashMap<>(); this.e = e; this.h = h; } public HashMap(java.util.Map map, final Equal e, final Hash h) { this(e, h); - for (K key : map.keySet()) { - set(key, map.get(key)); + for (Map.Entry entry : map.entrySet()) { + set(entry.getKey(), entry.getValue()); } } @@ -83,13 +76,13 @@ public HashMap(java.util.Map map, final Equal e, final Hash h) { * @param initialCapacity The initial capacity. */ public HashMap(final Equal e, final Hash h, final int initialCapacity) { - m = new java.util.HashMap, V>(initialCapacity); + m = new java.util.HashMap<>(initialCapacity); this.e = e; this.h = h; } public HashMap(java.util.Map map) { - this(map, Equal.anyEqual(), Hash.anyHash()); + this(map, Equal.anyEqual(), Hash.anyHash()); } /** @@ -101,7 +94,7 @@ public HashMap(java.util.Map map) { * @param loadFactor The load factor. */ public HashMap(final Equal e, final Hash h, final int initialCapacity, final float loadFactor) { - m = new java.util.HashMap, V>(initialCapacity, loadFactor); + m = new java.util.HashMap<>(initialCapacity, loadFactor); this.e = e; this.h = h; } @@ -121,7 +114,7 @@ public static HashMap hashMap() { * @return A new hash map. */ public static HashMap hashMap(final Equal e, final Hash h) { - return new HashMap(e, h); + return new HashMap<>(e, h); } /** @@ -152,7 +145,7 @@ public int hash(final K k) { * @return A potential value for the given key. */ public Option get(final K k) { - return fromNull(m.get(new Key(k, e, h))); + return fromNull(m.get(new Key(k))); } /** @@ -161,7 +154,7 @@ public Option get(final K k) { * @return A curried version of {@link #get(Object)}. */ public F> get() { - return k -> get(k); + return this::get; } /** @@ -178,7 +171,7 @@ public void clear() { * @return true if this hash map contains the given key, false otherwise. */ public boolean contains(final K k) { - return m.containsKey(new Key(k, e, h)); + return m.containsKey(new Key(k)); } /** @@ -187,10 +180,10 @@ public boolean contains(final K k) { * @return All key entries in this hash map. */ public List keys() { - final List.Buffer b = new List.Buffer(); + final List.Buffer b = new List.Buffer<>(); - for (final Key k : m.keySet()) { - b.snoc(k.k()); + for (final Key k : m.keySet()) { + b.snoc(k.k); } return b.toList(); @@ -202,7 +195,7 @@ public List keys() { * @return All values in this hash map. */ public List values() { - return keys().map(k -> m.get(new Key(k, e, h))); + return iterableList(m.values()); } /** @@ -231,7 +224,7 @@ public int size() { */ public void set(final K k, final V v) { if (v != null) { - m.put(new Key(k, e, h), v); + m.put(new Key(k), v); } } @@ -241,7 +234,7 @@ public void set(final K k, final V v) { * @param k The key to delete from this hash map. */ public void delete(final K k) { - m.remove(new Key(k, e, h)); + m.remove(new Key(k)); } /** @@ -251,13 +244,13 @@ public void delete(final K k) { * @return The value that was associated with the given key, if there was one. */ public Option getDelete(final K k) { - return fromNull(m.remove(new Key(k, e, h))); + return fromNull(m.remove(new Key(k))); } public HashMap map(F keyFunction, F valueFunction, Equal equal, Hash hash) { - final HashMap hashMap = new HashMap(equal, hash); + final HashMap hashMap = new HashMap<>(equal, hash); for (K key : keys()) { final A newKey = keyFunction.f(key); final B newValue = valueFunction.f(get(key).some()); @@ -268,27 +261,27 @@ public HashMap map(F keyFunction, public HashMap map(F keyFunction, F valueFunction) { - return map(keyFunction, valueFunction, Equal.anyEqual(), Hash.anyHash()); + return map(keyFunction, valueFunction, Equal.anyEqual(), Hash.anyHash()); } public HashMap map(F, P2> function, Equal equal, Hash hash) { - return from(toStream().map(function), equal, hash); + return iterableHashMap(equal, hash, toStream().map(function)); } public HashMap map(F, P2> function) { - return from(toStream().map(function)); + return iterableHashMap(toStream().map(function)); } public HashMap mapKeys(F keyFunction, Equal equal, Hash hash) { - return map(keyFunction, Function.identity(), equal, hash); + return map(keyFunction, Function.identity(), equal, hash); } public HashMap mapKeys(F function) { - return mapKeys(function, Equal.anyEqual(), Hash.anyHash()); + return mapKeys(function, Equal.anyEqual(), Hash.anyHash()); } public HashMap mapValues(F function) { - return map(Function.identity(), function, e, h); + return map(Function.identity(), function, e, h); } public void foreachDoEffect(Effect1> effect) { @@ -325,50 +318,30 @@ public Array> toArray() { } public java.util.Map toMap() { - final java.util.HashMap result = new java.util.HashMap(); + final java.util.HashMap result = new java.util.HashMap<>(); for (K key : keys()) { result.put(key, get(key).some()); } 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(Equal.anyEqual(), Hash.anyHash(), entries); - } - public static HashMap fromMap(java.util.Map map) { return fromMap(Equal.anyEqual(), Hash.anyHash(), map); } public static HashMap fromMap(Equal eq, Hash h, java.util.Map map) { - HashMap m = HashMap.hashMap(eq, h); + HashMap m = hashMap(eq, h); for (Map.Entry e: map.entrySet()) { m.set(e.getKey(), e.getValue()); } 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 */ public static HashMap iterableHashMap(final Equal equal, final Hash hash, final Iterable> entries) { - final HashMap map = new HashMap(equal, hash); + final HashMap map = new HashMap<>(equal, hash); for (P2 entry : entries) { map.set(entry._1(), entry._2()); } @@ -379,7 +352,7 @@ public static HashMap iterableHashMap(final Equal equal, final H * Converts the Iterable to a HashMap */ public static HashMap iterableHashMap(final Iterable> entries) { - return iterableHashMap(Equal.anyEqual(), Hash.anyHash(), entries); + return iterableHashMap(Equal.anyEqual(), Hash.anyHash(), entries); } /** diff --git a/core/src/main/java/fj/data/HashSet.java b/core/src/main/java/fj/data/HashSet.java index 1c6868e3..d7b2c832 100644 --- a/core/src/main/java/fj/data/HashSet.java +++ b/core/src/main/java/fj/data/HashSet.java @@ -36,7 +36,7 @@ public Iterator iterator() { * @param h The hashing strategy. */ public HashSet(final Equal e, final Hash h) { - m = new HashMap(e, h); + m = new HashMap<>(e, h); } /** @@ -47,7 +47,7 @@ public HashSet(final Equal e, final Hash h) { * @param initialCapacity The initial capacity. */ public HashSet(final Equal e, final Hash h, final int initialCapacity) { - m = new HashMap(e, h, initialCapacity); + m = new HashMap<>(e, h, initialCapacity); } /** @@ -59,7 +59,7 @@ public HashSet(final Equal e, final Hash h, final int initialCapacity) { * @param loadFactor The load factor. */ public HashSet(final Equal e, final Hash h, final int initialCapacity, final float loadFactor) { - m = new HashMap(e, h, initialCapacity, loadFactor); + m = new HashMap<>(e, h, initialCapacity, loadFactor); } /** @@ -87,7 +87,7 @@ public int hash(final A a) { * Creates a new HashSet using the given Equal and Hash */ public static HashSet empty(final Equal e, final Hash h) { - return new HashSet(e, h); + return new HashSet<>(e, h); } /** @@ -230,7 +230,7 @@ public java.util.List toJavaList() { } public java.util.Set toJavaSet() { - return new java.util.HashSet(toCollection()); + return new java.util.HashSet<>(toCollection()); } public static HashSet fromSet(java.util.Set s) { diff --git a/core/src/main/java/fj/data/IO.java b/core/src/main/java/fj/data/IO.java index eefd11b9..69434728 100644 --- a/core/src/main/java/fj/data/IO.java +++ b/core/src/main/java/fj/data/IO.java @@ -1,8 +1,8 @@ package fj.data; -/** - * Created by MarkPerry on 19/06/2014. - */ +import fj.F; +import fj.Unit; +import fj.function.Try0; import java.io.IOException; @@ -13,8 +13,37 @@ * * @param the type of the result produced by the IO */ -public interface IO { +@FunctionalInterface +public interface IO extends Try0 { + + A run() throws IOException; + + default A f() throws IOException { + return run(); + } + + 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 A run() throws IOException; + public static IO interact(F f) { + return () -> IOFunctions.interact(f).run(); + } } diff --git a/core/src/main/java/fj/data/IOFunctions.java b/core/src/main/java/fj/data/IOFunctions.java index bb2d7de1..9573deea 100644 --- a/core/src/main/java/fj/data/IOFunctions.java +++ b/core/src/main/java/fj/data/IOFunctions.java @@ -1,10 +1,21 @@ 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.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; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -13,10 +24,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 static fj.Bottom.errorF; +import static fj.Function.constant; +import static fj.Function.partialApply2; /** * IO monad for processing files, with main methods {@link #enumFileLines }, @@ -26,12 +36,15 @@ * * @author Martin Grotzke */ -public class IOFunctions { +public final class IOFunctions { private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + private IOFunctions() { + } + public static Try0 toTry(IO io) { - return () -> io.run(); + return io::run; } public static P1> p(IO io) { @@ -43,10 +56,10 @@ public static IO fromF(F0 p) { } public static IO fromTry(Try0 t) { - return () -> t.f(); + return t::f; } - public static final F> closeReader = r -> closeReader(r); + public static final F> closeReader = IOFunctions::closeReader; /** * Convert io to a SafeIO, throwing any IOException wrapped inside a RuntimeException @@ -71,12 +84,9 @@ public static A runSafe(IO io) { } public static IO closeReader(final Reader r) { - return new IO() { - @Override - public Unit run() throws IOException { - r.close(); - return Unit.unit(); - } + return () -> { + r.close(); + return Unit.unit(); }; } @@ -92,8 +102,8 @@ public Unit run() throws IOException { */ public static IO> enumFileLines(final File f, final Option encoding, final IterV i) { return bracket(bufferedReader(f, encoding) - , Function.>vary(closeReader) - , partialApply2(IOFunctions.lineReader(), i)); + , Function.vary(closeReader) + , partialApply2(IOFunctions.lineReader(), i)); } /** @@ -105,8 +115,8 @@ public static IO> enumFileLines(final File f, final Option< */ public static IO> enumFileCharChunks(final File f, final Option encoding, final IterV i) { return bracket(fileReader(f, encoding) - , Function.>vary(closeReader) - , partialApply2(IOFunctions.charChunkReader(), i)); + , Function.vary(closeReader) + , partialApply2(IOFunctions.charChunkReader(), i)); } /** @@ -118,12 +128,12 @@ public static IO> enumFileCharChunks(final File f, final Op */ public static IO> enumFileChars(final File f, final Option encoding, final IterV i) { return bracket(fileReader(f, encoding) - , Function.>vary(closeReader) - , partialApply2(IOFunctions.charChunkReader2(), i)); + , Function.vary(closeReader) + , partialApply2(IOFunctions.charChunkReader2(), i)); } public static IO bufferedReader(final File f, final Option encoding) { - return IOFunctions.map(fileReader(f, encoding), a -> new BufferedReader(a)); + return map(fileReader(f, encoding), BufferedReader::new); } public static IO fileReader(final File f, final Option encoding) { @@ -133,46 +143,34 @@ public static IO fileReader(final File f, final Option encoding }; } - public static final IO bracket(final IO init, final F> fin, final F> body) { - return new IO() { - @Override - public C run() throws IOException { - final A a = init.run(); - try { - return body.f(a).run(); - } catch (final IOException e) { - throw e; - } finally { - fin.f(a); - } + public static IO bracket(final IO init, final F> fin, final F> body) { + return () -> { + final A a = init.run(); + try(Closeable finAsCloseable = fin.f(a)::run) { + return body.f(a).run(); } }; } - public static final IO unit(final A a) { - return new IO() { - @Override - public A run() throws IOException { - return a; - } - }; + public static IO unit(final A a) { + return () -> a; } public static final IO ioUnit = unit(Unit.unit()); - public static final IO lazy(final F0 p) { + public static IO lazy(final F0 p) { return fromF(p); } - public static final IO lazy(final F f) { + public static IO lazy(final F f) { return () -> f.f(Unit.unit()); } - public static final SafeIO lazySafe(final F f) { + public static SafeIO lazySafe(final F f) { return () -> f.f(Unit.unit()); } - public static final SafeIO lazySafe(final F0 f) { + public static SafeIO lazySafe(final F0 f) { return f::f; } @@ -180,46 +178,37 @@ public static final 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 new F, IO>>>() { - @Override - public F, IO>> f(final BufferedReader r) { - return 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 new IO>() { - @Override - public IterV run() throws IOException { - 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., IterV>apply(input)); - i = i.fold(done, cont)._1(); - } - return i; - } - }; + 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; } - }; - } - }; + final Input input = Input.el(s); + final F, IterV>, P1>> cont = Function., IterV>apply(input).lazy(); + i = i.fold(done, cont)._1(); + } + return i; + }; + } } /** @@ -227,125 +216,115 @@ public IterV run() throws IOException { * (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 new F, IO>>>() { - @Override - public F, IO>> f(final Reader r) { - return 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 new IO>() { - @Override - public IterV run() throws IOException { - - 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., IterV>apply(input)); - i = i.fold(done, cont)._1(); - } - return i; - } - }; + if (numRead < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, numRead); } - }; - } - }; + final Input input = Input.el(buffer); + final F, IterV>, P1>> cont = + Function., IterV>apply(input).lazy(); + 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 new F, IO>>>() { - @Override - public F, IO>> f(final Reader r) { - return 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 new IO>() { - @Override - public IterV run() throws IOException { - - 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 (int c = 0; c < buffer.length; c++) { - final Input input = Input.el(buffer[c]); - final F, IterV>, IterV> cont = - Function., IterV>apply(input); - i = i.fold(done, cont); - } - } - 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); + } + } + return i; + }; + } } - public static final IO map(final IO io, final F f) { - return new IO() { - @Override - public B run() throws IOException { - return f.f(io.run()); - } - }; + public static IO map(final IO io, final F f) { + return () -> f.f(io.run()); } - public static final IO bind(final IO io, final F> f) { - return new IO() { - @Override - public B run() throws IOException { - return f.f(io.run()).run(); - } - }; + public static IO as(final IO io, final B b) { + return map(io, ignored -> b); + } + + public static IO voided(final IO io) { + return as(io, Unit.unit()); + } + + public static IO bind(final IO io, final F> f) { + return () -> f.f(io.run()).run(); + } + + public static IO when(final Boolean b, final IO io) { + return b ? io : ioUnit; + } + + public static IO unless(final Boolean b, final IO io) { + return when(!b, io); } /** @@ -353,15 +332,15 @@ public B run() throws IOException { */ public static IO> sequence(List> list) { F2, IO>, IO>> f2 = (io, ioList) -> - IOFunctions.bind(ioList, (xs) -> map(io, x -> List.cons(x, xs))); - return list.foldRight(f2, IOFunctions.unit(List.nil())); + bind(ioList, (xs) -> map(io, x -> List.cons(x, xs))); + return list.foldRight(f2, unit(List.nil())); } public static IO> sequence(Stream> stream) { F2>, IO, IO>> f2 = (ioList, io) -> - IOFunctions.bind(ioList, (xs) -> map(io, x -> Stream.cons(x, () -> xs))); - return stream.foldLeft(f2, IOFunctions.unit(Stream.nil())); + bind(ioList, (xs) -> map(io, x -> Stream.cons(x, () -> xs))); + return stream.foldLeft(f2, unit(Stream.nil())); } @@ -370,7 +349,7 @@ public static IO join(IO> io1) { } public static SafeIO> toSafeValidation(IO io) { - return () -> Try.f(() -> io.run())._1(); + return () -> Try.f(io::run)._1(); } public static IO append(final IO io1, final IO io2) { @@ -399,59 +378,53 @@ public static IO flatMap(final IO io, final F> f) { * @param transform Function to change line value */ public static IO interactWhile(F condition, F transform) { - Stream> s1 = Stream.repeat(IOFunctions.stdinReadLine()); + Stream> s1 = Stream.repeat(stdinReadLine()); IO> io = sequenceWhile(s1, condition); return () -> runSafe(io).foreach(s -> runSafe(stdoutPrintln(transform.f(s)))); } public static IO> sequenceWhileEager(final Stream> stream, final F f) { - return new IO>() { - @Override - public Stream run() throws IOException { - boolean loop = true; - Stream> input = stream; - Stream result = Stream.nil(); - while (loop) { - if (input.isEmpty()) { + return () -> { + boolean loop = true; + Stream> input = stream; + Stream result = Stream.nil(); + while (loop) { + if (input.isEmpty()) { + loop = false; + } else { + A a = input.head().run(); + if (!f.f(a)) { loop = false; } else { - A a = input.head().run(); - if (!f.f(a)) { - loop = false; - } else { - input = input.tail()._1(); - result = result.cons(a); - } + input = input.tail()._1(); + result = result.cons(a); } } - return result.reverse(); } + return result.reverse(); }; } public static IO> sequenceWhile(final Stream> stream, final F f) { - return new IO>() { - @Override - public Stream run() throws IOException { - if (stream.isEmpty()) { + return () -> { + if (stream.isEmpty()) { + return Stream.nil(); + } else { + IO io = stream.head(); + A a = io.run(); + if (!f.f(a)) { return Stream.nil(); } else { - IO io = stream.head(); - A a = io.run(); - if (!f.f(a)) { - return Stream.nil(); - } else { - IO> io2 = sequenceWhile(stream.tail()._1(), f); - SafeIO> s3 = toSafe(() -> io2.run()); - return Stream.cons(a, () -> s3.run()); - } + IO> io2 = sequenceWhile(stream.tail()._1(), f); + SafeIO> s3 = toSafe(io2::run); + return Stream.cons(a, s3::run); } } }; } public static IO apply(IO io, IO> iof) { - return bind(iof, f -> map(io, a -> f.f(a))); + return bind(iof, f -> map(io, f)); } public static IO liftM2(IO ioa, IO iob, F2 f) { @@ -463,13 +436,13 @@ public static IO> replicateM(IO ioa, int n) { } public static IO>> readerState() { - return () -> State.unit((BufferedReader r) -> P.p(r, Try.f((BufferedReader r2) -> r2.readLine()).f(r))); + return () -> State.unit((BufferedReader r) -> P.p(r, Try.f((Try1) BufferedReader::readLine).f(r))); } public static final BufferedReader stdinBufferedReader = new BufferedReader(new InputStreamReader(System.in)); public static IO stdinReadLine() { - return () -> stdinBufferedReader.readLine(); + return stdinBufferedReader::readLine; } public static IO stdoutPrintln(final String s) { @@ -479,11 +452,16 @@ public static IO stdoutPrintln(final String s) { }; } + public static IO stdoutPrint(final String s) { + return () -> { + System.out.print(s); + return Unit.unit(); + }; + } + public static IO getContents() { - Stream> s = Stream.>repeat(() -> (int) stdinBufferedReader.read()); - return IOFunctions.map(sequenceWhile(s, i -> i != -1), s2 -> LazyString.fromStream(s2.map(i -> { - return (char) i.intValue(); - }))); + Stream> s = Stream.repeat(() -> stdinBufferedReader.read()); + return map(sequenceWhile(s, i -> i != -1), s2 -> LazyString.fromStream(s2.map(i -> (char) i.intValue()))); } public static IO interact(F f) { 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 5364f49e..00000000 --- a/core/src/main/java/fj/data/IOW.java +++ /dev/null @@ -1,50 +0,0 @@ -package fj.data; - -import fj.F; -import fj.Unit; - -import java.io.IOException; - -/** - * Created by MarkPerry on 9/06/2015. - */ -public class IOW implements IO { - - private 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 IOW map(F f) { - return lift(() -> f.f(io.run())); - } - - public IOW bind(F> f) throws IOException { - return lift(f.f(io.run())); - } - - public IOW append(IO iob) { - return lift(() -> { - io.run(); - return iob.run(); - }); - } - - public IOW getContents() { - return lift(() -> IOFunctions.getContents().run()); - } - - public IOW interact(F f) { - return IOW.lift(() -> IOFunctions.interact(f).run()); - } -} diff --git a/core/src/main/java/fj/data/IterableW.java b/core/src/main/java/fj/data/IterableW.java index 52be7d6b..db75dbb5 100644 --- a/core/src/main/java/fj/data/IterableW.java +++ b/core/src/main/java/fj/data/IterableW.java @@ -5,8 +5,6 @@ import fj.Equal; import fj.F; import fj.F2; -import fj.F3; -import fj.P1; import fj.P2; import static fj.Function.curry; import static fj.Function.identity; @@ -35,7 +33,7 @@ private IterableW(final Iterable i) { * @return An iterable equipped with some useful functions. */ public static IterableW wrap(final Iterable a) { - return new IterableW(a); + return new IterableW<>(a); } /** @@ -44,7 +42,7 @@ public static IterableW wrap(final Iterable a) { * @return A function that returns the given iterable, wrapped. */ public static > F> wrap() { - return a -> wrap(a); + return IterableW::wrap; } /** @@ -75,7 +73,7 @@ public static F> iterable(final F f) { * @return A transformation from a function to the equivalent Iterable-valued function. */ public static F, F>> arrow() { - return f -> iterable(f); + return IterableW::iterable; } /** @@ -95,7 +93,7 @@ public > IterableW bind(final F f) { * @return A new iterable after applying the given iterable function to the wrapped iterable. */ public IterableW apply(final Iterable> f) { - return wrap(f).bind(f1 -> map(f1)); + return wrap(f).bind(this::map); } /** @@ -130,9 +128,9 @@ public static F, F, IterableW>> liftM2(fina public static > IterableW> sequence(final Iterable as) { final Stream ts = iterableStream(as); return ts.isEmpty() ? - iterable(wrap(Option.none())) : + iterable(wrap(Option.none())) : wrap(ts.head()).bind(a -> - sequence(ts.tail().map(IterableW.>wrap())._1()).bind(as2 -> + sequence(ts.tail().map(IterableW.wrap())._1()).bind(as2 -> iterable(wrap(Stream.cons(a, () -> iterableStream(as2)))) ) ); @@ -145,7 +143,7 @@ public static > IterableW> sequence(final * @return a function that binds a given function across a given iterable. */ public static > F, F, IterableW>> bind() { - return a -> f -> a.bind(f); + return a -> a::bind; } /** @@ -165,7 +163,7 @@ public static > IterableW join(final Iterable as) * @return a function that joins an Iterable of Iterables into a single Iterable. */ public static > F, IterableW> join() { - return a -> join(a); + return IterableW::join; } /** @@ -324,7 +322,7 @@ public boolean isEmpty() { return iterableStream(IterableW.this).isEmpty(); } - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public boolean contains(final Object o) { return iterableStream(IterableW.this).exists(Equal.anyEqual().eq((A) o)); } @@ -337,7 +335,7 @@ public Object[] toArray() { return Array.iterableArray(iterableStream(IterableW.this)).array(); } - @SuppressWarnings({"SuspiciousToArrayCall"}) + @SuppressWarnings("SuspiciousToArrayCall") public T[] toArray(final T[] a) { return iterableStream(IterableW.this).toCollection().toArray(a); } @@ -420,7 +418,7 @@ public ListIterator listIterator(final int index) { } public List subList(final int fromIndex, final int toIndex) { - return wrap(Stream.iterableStream(IterableW.this).drop(fromIndex).take(toIndex - fromIndex)).toStandardList(); + return wrap(iterableStream(IterableW.this).drop(fromIndex).take(toIndex - fromIndex)).toStandardList(); } private ListIterator toListIterator(final Option> z) { diff --git a/core/src/main/java/fj/data/Iteratee.java b/core/src/main/java/fj/data/Iteratee.java index 9eaeb95e..e6177068 100644 --- a/core/src/main/java/fj/data/Iteratee.java +++ b/core/src/main/java/fj/data/Iteratee.java @@ -2,27 +2,22 @@ import fj.F; import fj.F0; -import fj.F1Functions; import fj.Function; import fj.P; -import fj.P1; import fj.P2; import fj.Unit; -/** - * - */ public final class Iteratee { /** The input to an iteratee. */ - public static abstract class Input { + public abstract static class Input { Input() {} // sealed public abstract Z apply(final F0 empty, final F0> el, final F0 eof); /** Input that has no values available */ - public static final Input empty() { + public static Input empty() { return new Input() { @Override public Z apply(final F0 empty, final F0> el, final F0 eof) { @@ -32,7 +27,7 @@ public Z apply(final F0 empty, final F0> el, final F0 eof) { } /** Input that is exhausted */ - public static final Input eof() { + public static Input eof() { return new Input() { @Override public Z apply(final F0 empty, final F0> el, final F0 eof) { @@ -42,7 +37,7 @@ public Z apply(final F0 empty, final F0> el, final F0 eof) { } /** Input that has a value available */ - public static final Input el(final E element) { + public static Input el(final E element) { return new Input() { @Override public Z apply(final F0 empty, final F0> el, final F0 eof) { @@ -53,7 +48,7 @@ public Z apply(final F0 empty, final F0> el, final F0 eof) { } /** A pure iteratee computation which is either done or needs more input */ - public static abstract class IterV { + public abstract static class IterV { IterV() {} // sealed @@ -83,28 +78,26 @@ 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, IterV>, Option> cont = Function.constant(Option.none()); + final F>, Option> done = P2.>__1().andThen(Option.some_()); + final F, IterV>, Option> cont = Function.constant(Option.none()); @Override public Option f(final IterV i) { return i.fold(done, cont); } }; - final F>, A> done = P2.>__1(); + final F>, A> done = P2.__1(); final F, IterV>, A> cont = - k -> runCont.f(k.f(Input.eof())).valueE("diverging iteratee"); + k -> runCont.f(k.f(Input.eof())).valueE("diverging iteratee"); return fold(done, cont); } /** TODO more documentation */ public final IterV bind(final F> f) { final F>, IterV> done = - new F>, IterV>() { - @Override - public IterV f(final P2> xe) { + xe -> { final Input e = xe._2(); - final F>, IterV> done = + final F>, IterV> done1 = y_ -> { final B y = y_._1(); return done(y, e); @@ -112,16 +105,15 @@ public IterV f(final P2> xe) { final F, IterV>, IterV> cont = k -> k.f(e); final A x = xe._1(); - return f.f(x).fold(done, cont); - } - }; + return f.f(x).fold(done1, cont); + }; final F, IterV>, IterV> cont = k -> cont(e -> k.f(e).bind(f)); return this.fold(done, cont); } /** An iteratee that counts and consumes the elements of the input */ - public static final IterV length() { + public static IterV length() { final F, IterV>> step = new F, IterV>>() { final F, IterV>> step = this; @@ -141,7 +133,7 @@ public F, IterV> f(final Integer acc) { } /** An iteratee that skips the first n elements of the input */ - public static final IterV drop(final int n) { + public static IterV drop(final int n) { final F, IterV> step = new F, IterV>() { final F, IterV> step = this; @@ -158,19 +150,19 @@ public IterV f(final Input s) { } }; return n == 0 - ? done(Unit.unit(), Input.empty()) + ? done(Unit.unit(), Input.empty()) : cont(step); } /** An iteratee that consumes the head of the input */ - public static final IterV> head() { + public static IterV> head() { final F, IterV>> step = new F, IterV>>() { final F, IterV>> step = this; final F0>> empty = () -> cont(step); - final F0>>> el = () -> e -> done(Option.some(e), Input.empty()); + final F0>>> el = () -> e -> done(Option.some(e), Input.empty()); final F0>> eof = () -> done(Option.none(), Input.eof()); @@ -183,14 +175,14 @@ public IterV> f(final Input s) { } /** An iteratee that returns the first element of the input */ - public static final IterV> peek() { + public static IterV> peek() { final F, IterV>> step = new F, IterV>>() { final F, IterV>> step = this; final F0>> empty = () -> cont(step); - final F0>>> el = () -> e -> done(Option.some(e), Input.el(e)); + final F0>>> el = () -> e -> done(Option.some(e), Input.el(e)); final F0>> eof = () -> done(Option.none(), Input.eof()); @@ -204,7 +196,7 @@ public IterV> f(final Input s) { /** An iteratee that consumes the input elements and returns them as a list in reverse order, * so that the last line is the first element. This allows to build a list from 2 iteratees. */ - public static final IterV> list() { + public static IterV> list() { final F, F, IterV>>> step = new F, F, IterV>>>() { final F, F, IterV>>> step = this; @@ -220,7 +212,7 @@ public F, IterV>> f(final List acc) { return s -> s.apply(empty, el, eof); } }; - return cont(step.f(List. nil())); + return cont(step.f(List.nil())); } } diff --git a/core/src/main/java/fj/data/Java.java b/core/src/main/java/fj/data/Java.java index 29c5714c..b3cb7e7f 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -4,9 +4,6 @@ import static fj.P.p; -import fj.function.Effect1; - -import static fj.data.List.list; import static fj.data.Option.some; import java.util.Arrays; @@ -38,8 +35,6 @@ /** * Functions that convert between types from the core Java API. - * - * @version %build.number% */ public final class Java { private Java() { @@ -54,7 +49,7 @@ private Java() { * @return A function that converts lists to array lists. */ public static F, ArrayList> List_ArrayList() { - return as -> new ArrayList(as.toCollection()); + return as -> new ArrayList<>(as.toCollection()); } /** @@ -62,9 +57,7 @@ public static F, ArrayList> List_ArrayList() { */ public static final F, BitSet> List_BitSet = bs -> { final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreachDoEffect(bi -> { - s.set(bi._2(), bi._1()); - }); + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); return s; }; @@ -83,7 +76,7 @@ public static > F, EnumSet> List_EnumSet() { * @return A function that converts lists to hash sets. */ public static F, HashSet> List_HashSet() { - return as -> new HashSet(as.toCollection()); + return as -> new HashSet<>(as.toCollection()); } /** @@ -92,7 +85,7 @@ public static F, HashSet> List_HashSet() { * @return A function that converts lists to linked hash sets. */ public static F, LinkedHashSet> List_LinkedHashSet() { - return as -> new LinkedHashSet(as.toCollection()); + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -101,7 +94,7 @@ public static F, LinkedHashSet> List_LinkedHashSet() { * @return A function that converts lists to linked lists. */ public static F, LinkedList> List_LinkedList() { - return as -> new LinkedList(as.toCollection()); + return as -> new LinkedList<>(as.toCollection()); } /** @@ -110,7 +103,7 @@ public static F, LinkedList> List_LinkedList() { * @return A function that converts lists to priority queues. */ public static F, PriorityQueue> List_PriorityQueue() { - return as -> new PriorityQueue(as.toCollection()); + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -120,7 +113,7 @@ public static F, PriorityQueue> List_PriorityQueue() { */ public static F, Stack> List_Stack() { return as -> { - final Stack s = new Stack(); + final Stack s = new Stack<>(); s.addAll(as.toCollection()); return s; }; @@ -132,7 +125,7 @@ public static F, Stack> List_Stack() { * @return A function that converts lists to stacks. */ public static F, TreeSet> List_TreeSet() { - return as -> new TreeSet(as.toCollection()); + return as -> new TreeSet<>(as.toCollection()); } /** @@ -141,7 +134,7 @@ public static F, TreeSet> List_TreeSet() { * @return A function that converts lists to vectors. */ public static F, Vector> List_Vector() { - return as -> new Vector(as.toCollection()); + return as -> new Vector<>(as.toCollection()); } /** @@ -151,7 +144,7 @@ public static F, Vector> List_Vector() { * @return A function that converts lists to array blocking queue. */ public static F, ArrayBlockingQueue> List_ArrayBlockingQueue(final boolean fair) { - return as -> new ArrayBlockingQueue(as.length(), fair, as.toCollection()); + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -160,7 +153,7 @@ public static F, ArrayBlockingQueue> List_ArrayBlockingQueue(fina * @return A function that converts lists to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> List_ConcurrentLinkedQueue() { - return as -> new ConcurrentLinkedQueue(as.toCollection()); + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -169,7 +162,7 @@ public static F, ConcurrentLinkedQueue> List_ConcurrentLinkedQueu * @return A function that converts lists to copy on write array lists. */ public static F, CopyOnWriteArrayList> List_CopyOnWriteArrayList() { - return as -> new CopyOnWriteArrayList(as.toCollection()); + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -178,7 +171,7 @@ public static F, CopyOnWriteArrayList> List_CopyOnWriteArrayList( * @return A function that converts lists to copy on write array sets. */ public static F, CopyOnWriteArraySet> List_CopyOnWriteArraySet() { - return as -> new CopyOnWriteArraySet(as.toCollection()); + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -187,7 +180,7 @@ public static F, CopyOnWriteArraySet> List_CopyOnWriteArraySet() * @return A function that converts lists to delay queues. */ public static F, DelayQueue> List_DelayQueue() { - return as -> new DelayQueue(as.toCollection()); + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -196,7 +189,7 @@ public static F, DelayQueue> List_DelayQueue() { * @return A function that converts lists to linked blocking queues. */ public static F, LinkedBlockingQueue> List_LinkedBlockingQueue() { - return as -> new LinkedBlockingQueue(as.toCollection()); + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -205,7 +198,7 @@ public static F, LinkedBlockingQueue> List_LinkedBlockingQueue() * @return A function that converts lists to priority blocking queues. */ public static F, PriorityBlockingQueue> List_PriorityBlockingQueue() { - return as -> new PriorityBlockingQueue(as.toCollection()); + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -216,7 +209,7 @@ public static F, PriorityBlockingQueue> List_PriorityBlockingQueu */ public static F, SynchronousQueue> List_SynchronousQueue(final boolean fair) { return as -> { - final SynchronousQueue q = new SynchronousQueue(fair); + final SynchronousQueue q = new SynchronousQueue<>(fair); q.addAll(as.toCollection()); return q; }; @@ -232,7 +225,7 @@ public static F, SynchronousQueue> List_SynchronousQueue(final bo * @return A function that converts arrays to array lists. */ public static F, ArrayList> Array_ArrayList() { - return as -> new ArrayList(as.toCollection()); + return Array::toJavaList; } /** @@ -241,9 +234,7 @@ public static F, ArrayList> Array_ArrayList() { public static final F, BitSet> Array_BitSet = bs -> { final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreachDoEffect(bi -> { - s.set(bi._2(), bi._1()); - }); + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); return s; }; @@ -253,7 +244,7 @@ public static F, ArrayList> Array_ArrayList() { * @return A function that converts arrays to enum sets. */ public static > F, EnumSet> Array_EnumSet() { - return as -> copyOf(as.toCollection()); + return as -> copyOf(as.asJavaList()); } /** @@ -262,7 +253,7 @@ public static > F, EnumSet> Array_EnumSet() { * @return A function that converts arrays to hash sets. */ public static F, HashSet> Array_HashSet() { - return as -> new HashSet(as.toCollection()); + return as -> new HashSet<>(as.asJavaList()); } /** @@ -271,7 +262,7 @@ public static F, HashSet> Array_HashSet() { * @return A function that converts arrays to linked hash sets. */ public static F, LinkedHashSet> Array_LinkedHashSet() { - return as -> new LinkedHashSet(as.toCollection()); + return as -> new LinkedHashSet<>(as.asJavaList()); } /** @@ -280,7 +271,7 @@ public static F, LinkedHashSet> Array_LinkedHashSet() { * @return A function that converts arrays to linked lists. */ public static F, LinkedList> Array_LinkedList() { - return as -> new LinkedList(as.toCollection()); + return as -> new LinkedList<>(as.asJavaList()); } /** @@ -289,7 +280,7 @@ public static F, LinkedList> Array_LinkedList() { * @return A function that converts arrays to priority queues. */ public static F, PriorityQueue> Array_PriorityQueue() { - return as -> new PriorityQueue(as.toCollection()); + return as -> new PriorityQueue<>(as.asJavaList()); } /** @@ -299,8 +290,8 @@ public static F, PriorityQueue> Array_PriorityQueue() { */ public static F, Stack> Array_Stack() { return as -> { - final Stack s = new Stack(); - s.addAll(as.toCollection()); + final Stack s = new Stack<>(); + s.addAll(as.asJavaList()); return s; }; } @@ -311,7 +302,7 @@ public static F, Stack> Array_Stack() { * @return A function that converts arrays to tree sets. */ public static F, TreeSet> Array_TreeSet() { - return as -> new TreeSet(as.toCollection()); + return as -> new TreeSet<>(as.asJavaList()); } /** @@ -320,7 +311,7 @@ public static F, TreeSet> Array_TreeSet() { * @return A function that converts arrays to vectors. */ public static F, Vector> Array_Vector() { - return as -> new Vector(as.toCollection()); + return as -> new Vector<>(as.asJavaList()); } /** @@ -330,7 +321,7 @@ public static F, Vector> Array_Vector() { * @return A function that converts arrays to array blocking queues. */ public static F, ArrayBlockingQueue> Array_ArrayBlockingQueue(final boolean fair) { - return as -> new ArrayBlockingQueue(as.length(), fair, as.toCollection()); + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.asJavaList()); } /** @@ -339,7 +330,7 @@ public static F, ArrayBlockingQueue> Array_ArrayBlockingQueue(fi * @return A function that converts arrays to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Array_ConcurrentLinkedQueue() { - return as -> new ConcurrentLinkedQueue(as.toCollection()); + return as -> new ConcurrentLinkedQueue<>(as.asJavaList()); } /** @@ -348,7 +339,7 @@ public static F, ConcurrentLinkedQueue> Array_ConcurrentLinkedQu * @return A function that converts arrays to copy on write array lists. */ public static F, CopyOnWriteArrayList> Array_CopyOnWriteArrayList() { - return as -> new CopyOnWriteArrayList(as.toCollection()); + return as -> new CopyOnWriteArrayList<>(as.asJavaList()); } /** @@ -357,7 +348,7 @@ public static F, CopyOnWriteArrayList> Array_CopyOnWriteArrayLis * @return A function that converts arrays to copy on write array sets. */ public static F, CopyOnWriteArraySet> Array_CopyOnWriteArraySet() { - return as -> new CopyOnWriteArraySet(as.toCollection()); + return as -> new CopyOnWriteArraySet<>(as.asJavaList()); } /** @@ -366,7 +357,7 @@ public static F, CopyOnWriteArraySet> Array_CopyOnWriteArraySet( * @return A function that converts arrays to delay queues. */ public static F, DelayQueue> Array_DelayQueue() { - return as -> new DelayQueue(as.toCollection()); + return as -> new DelayQueue<>(as.asJavaList()); } /** @@ -375,7 +366,7 @@ public static F, DelayQueue> Array_DelayQueue() * @return A function that converts arrays to linked blocking queues. */ public static F, LinkedBlockingQueue> Array_LinkedBlockingQueue() { - return as -> new LinkedBlockingQueue(as.toCollection()); + return as -> new LinkedBlockingQueue<>(as.asJavaList()); } /** @@ -384,7 +375,7 @@ public static F, LinkedBlockingQueue> Array_LinkedBlockingQueue( * @return A function that converts arrays to priority blocking queues. */ public static F, PriorityBlockingQueue> Array_PriorityBlockingQueue() { - return as -> new PriorityBlockingQueue(as.toCollection()); + return as -> new PriorityBlockingQueue<>(as.asJavaList()); } /** @@ -395,8 +386,8 @@ public static F, PriorityBlockingQueue> Array_PriorityBlockingQu */ public static F, SynchronousQueue> Array_SynchronousQueue(final boolean fair) { return as -> { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(as.toCollection()); + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(as.asJavaList()); return q; }; } @@ -411,27 +402,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; } /** @@ -440,7 +411,7 @@ public void remove() { * @return A function that converts streams to array lists. */ public static F, ArrayList> Stream_ArrayList() { - return as -> new ArrayList(as.toCollection()); + return as -> new ArrayList<>(as.toCollection()); } /** @@ -448,9 +419,7 @@ public static F, ArrayList> Stream_ArrayList() { */ public static final F, BitSet> Stream_BitSet = bs -> { final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreachDoEffect(bi -> { - s.set(bi._2(), bi._1()); - }); + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); return s; }; @@ -469,7 +438,7 @@ public static > F, EnumSet> Stream_EnumSet() { * @return A function that converts streams to hash sets. */ public static F, HashSet> Stream_HashSet() { - return as -> new HashSet(as.toCollection()); + return as -> new HashSet<>(as.toCollection()); } /** @@ -478,7 +447,7 @@ public static F, HashSet> Stream_HashSet() { * @return A function that converts streams to linked hash sets. */ public static F, LinkedHashSet> Stream_LinkedHashSet() { - return as -> new LinkedHashSet(as.toCollection()); + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -487,7 +456,7 @@ public static F, LinkedHashSet> Stream_LinkedHashSet() { * @return A function that converts streams to linked lists. */ public static F, LinkedList> Stream_LinkedList() { - return as -> new LinkedList(as.toCollection()); + return as -> new LinkedList<>(as.toCollection()); } /** @@ -496,7 +465,7 @@ public static F, LinkedList> Stream_LinkedList() { * @return A function that converts streams to priority queues. */ public static F, PriorityQueue> Stream_PriorityQueue() { - return as -> new PriorityQueue(as.toCollection()); + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -506,7 +475,7 @@ public static F, PriorityQueue> Stream_PriorityQueue() { */ public static F, Stack> Stream_Stack() { return as -> { - final Stack s = new Stack(); + final Stack s = new Stack<>(); s.addAll(as.toCollection()); return s; }; @@ -518,7 +487,7 @@ public static F, Stack> Stream_Stack() { * @return A function that converts streams to tree sets. */ public static F, TreeSet> Stream_TreeSet() { - return as -> new TreeSet(as.toCollection()); + return as -> new TreeSet<>(as.toCollection()); } /** @@ -527,7 +496,7 @@ public static F, TreeSet> Stream_TreeSet() { * @return A function that converts streams to vectors. */ public static F, Vector> Stream_Vector() { - return as -> new Vector(as.toCollection()); + return as -> new Vector<>(as.toCollection()); } /** @@ -537,7 +506,7 @@ public static F, Vector> Stream_Vector() { * @return A function that converts streams to array blocking queues. */ public static F, ArrayBlockingQueue> Stream_ArrayBlockingQueue(final boolean fair) { - return as -> new ArrayBlockingQueue(as.length(), fair, as.toCollection()); + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -546,7 +515,7 @@ public static F, ArrayBlockingQueue> Stream_ArrayBlockingQueue( * @return A function that converts streams to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Stream_ConcurrentLinkedQueue() { - return as -> new ConcurrentLinkedQueue(as.toCollection()); + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -555,7 +524,7 @@ public static F, ConcurrentLinkedQueue> Stream_ConcurrentLinked * @return A function that converts streams to copy on write array lists. */ public static F, CopyOnWriteArrayList> Stream_CopyOnWriteArrayList() { - return as -> new CopyOnWriteArrayList(as.toCollection()); + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -564,7 +533,7 @@ public static F, CopyOnWriteArrayList> Stream_CopyOnWriteArrayL * @return A function that converts streams to copy on write array sets. */ public static F, CopyOnWriteArraySet> Stream_CopyOnWriteArraySet() { - return as -> new CopyOnWriteArraySet(as.toCollection()); + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -573,7 +542,7 @@ public static F, CopyOnWriteArraySet> Stream_CopyOnWriteArraySe * @return A function that converts streams to delay queues. */ public static F, DelayQueue> Stream_DelayQueue() { - return as -> new DelayQueue(as.toCollection()); + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -582,7 +551,7 @@ public static F, DelayQueue> Stream_DelayQueue( * @return A function that converts streams to linked blocking queues. */ public static F, LinkedBlockingQueue> Stream_LinkedBlockingQueue() { - return as -> new LinkedBlockingQueue(as.toCollection()); + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -591,7 +560,7 @@ public static F, LinkedBlockingQueue> Stream_LinkedBlockingQueu * @return A function that converts streams to priority blocking queues. */ public static F, PriorityBlockingQueue> Stream_PriorityBlockingQueue() { - return as -> new PriorityBlockingQueue(as.toCollection()); + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -602,7 +571,7 @@ public static F, PriorityBlockingQueue> Stream_PriorityBlocking */ public static F, SynchronousQueue> Stream_SynchronousQueue(final boolean fair) { return as -> { - final SynchronousQueue q = new SynchronousQueue(fair); + final SynchronousQueue q = new SynchronousQueue<>(fair); q.addAll(as.toCollection()); return q; }; @@ -618,7 +587,7 @@ public static F, SynchronousQueue> Stream_SynchronousQueue(fina * @return A function that converts options to array lists. */ public static F, ArrayList> Option_ArrayList() { - return as -> new ArrayList(as.toCollection()); + return as -> new ArrayList<>(as.toCollection()); } /** @@ -649,7 +618,7 @@ public static > F, EnumSet> Option_EnumSet() { * @return A function that converts options to hash sets. */ public static F, HashSet> Option_HashSet() { - return as -> new HashSet(as.toCollection()); + return as -> new HashSet<>(as.toCollection()); } /** @@ -658,7 +627,7 @@ public static F, HashSet> Option_HashSet() { * @return A function that converts options to linked hash sets. */ public static F, LinkedHashSet> Option_LinkedHashSet() { - return as -> new LinkedHashSet(as.toCollection()); + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -667,7 +636,7 @@ public static F, LinkedHashSet> Option_LinkedHashSet() { * @return A function that converts options to linked lists. */ public static F, LinkedList> Option_LinkedList() { - return as -> new LinkedList(as.toCollection()); + return as -> new LinkedList<>(as.toCollection()); } /** @@ -676,7 +645,7 @@ public static F, LinkedList> Option_LinkedList() { * @return A function that converts options to priority queues. */ public static F, PriorityQueue> Option_PriorityQueue() { - return as -> new PriorityQueue(as.toCollection()); + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -686,7 +655,7 @@ public static F, PriorityQueue> Option_PriorityQueue() { */ public static F, Stack> Option_Stack() { return as -> { - final Stack s = new Stack(); + final Stack s = new Stack<>(); s.addAll(as.toCollection()); return s; }; @@ -698,7 +667,7 @@ public static F, Stack> Option_Stack() { * @return A function that converts options to tree sets. */ public static F, TreeSet> Option_TreeSet() { - return as -> new TreeSet(as.toCollection()); + return as -> new TreeSet<>(as.toCollection()); } /** @@ -707,7 +676,7 @@ public static F, TreeSet> Option_TreeSet() { * @return A function that converts options to vectors. */ public static F, Vector> Option_Vector() { - return as -> new Vector(as.toCollection()); + return as -> new Vector<>(as.toCollection()); } /** @@ -717,7 +686,7 @@ public static F, Vector> Option_Vector() { * @return A function that converts options to array blocking queues. */ public static F, ArrayBlockingQueue> Option_ArrayBlockingQueue(final boolean fair) { - return as -> new ArrayBlockingQueue(as.length(), fair, as.toCollection()); + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -726,7 +695,7 @@ public static F, ArrayBlockingQueue> Option_ArrayBlockingQueue( * @return A function that converts options to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Option_ConcurrentLinkedQueue() { - return as -> new ConcurrentLinkedQueue(as.toCollection()); + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -735,7 +704,7 @@ public static F, ConcurrentLinkedQueue> Option_ConcurrentLinked * @return A function that converts options to copy on write array lists. */ public static F, CopyOnWriteArrayList> Option_CopyOnWriteArrayList() { - return as -> new CopyOnWriteArrayList(as.toCollection()); + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -744,7 +713,7 @@ public static F, CopyOnWriteArrayList> Option_CopyOnWriteArrayL * @return A function that converts options to copy on write array sets. */ public static F, CopyOnWriteArraySet> Option_CopyOnWriteArraySet() { - return as -> new CopyOnWriteArraySet(as.toCollection()); + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -753,7 +722,7 @@ public static F, CopyOnWriteArraySet> Option_CopyOnWriteArraySe * @return A function that converts options to delay queues. */ public static F, DelayQueue> Option_DelayQueue() { - return as -> new DelayQueue(as.toCollection()); + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -762,7 +731,7 @@ public static F, DelayQueue> Option_DelayQueue( * @return A function that converts options to linked blocking queues. */ public static F, LinkedBlockingQueue> Option_LinkedBlockingQueue() { - return as -> new LinkedBlockingQueue(as.toCollection()); + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -771,7 +740,7 @@ public static F, LinkedBlockingQueue> Option_LinkedBlockingQueu * @return A function that converts options to priority blocking queues. */ public static F, PriorityBlockingQueue> Option_PriorityBlockingQueue() { - return as -> new PriorityBlockingQueue(as.toCollection()); + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -782,7 +751,7 @@ public static F, PriorityBlockingQueue> Option_PriorityBlocking */ public static F, SynchronousQueue> Option_SynchronousQueue(final boolean fair) { return as -> { - final SynchronousQueue q = new SynchronousQueue(fair); + final SynchronousQueue q = new SynchronousQueue<>(fair); q.addAll(as.toCollection()); return q; }; @@ -798,7 +767,7 @@ public static F, SynchronousQueue> Option_SynchronousQueue(fina * @return A function that converts eithers to array lists. */ public static F, ArrayList> Either_ArrayListA() { - return Function.compose(Java.Option_ArrayList(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_ArrayList(), Conversions.Either_OptionA()); } /** @@ -807,7 +776,7 @@ public static F, ArrayList> Either_ArrayListA() { * @return A function that converts eithers to array lists. */ public static F, ArrayList> Either_ArrayListB() { - return Function.compose(Java.Option_ArrayList(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_ArrayList(), Conversions.Either_OptionB()); } /** @@ -816,7 +785,7 @@ public static F, ArrayList> Either_ArrayListB() { * @return A function that converts eithers to bit sets. */ public static F, BitSet> Either_BitSetA() { - return Function.compose(Option_BitSet, Conversions.Either_OptionA()); + return Function.compose(Option_BitSet, Conversions.Either_OptionA()); } /** @@ -825,7 +794,7 @@ public static F, BitSet> Either_BitSetA() { * @return A function that converts eithers to bit sets. */ public static F, BitSet> Either_BitSetB() { - return Function.compose(Option_BitSet, Conversions.Either_OptionB()); + return Function.compose(Option_BitSet, Conversions.Either_OptionB()); } /** @@ -852,7 +821,7 @@ public static > F, EnumSet> Either_EnumSetB * @return A function that converts eithers to hash sets. */ public static F, HashSet> Either_HashSetA() { - return Function.compose(Java.Option_HashSet(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_HashSet(), Conversions.Either_OptionA()); } /** @@ -861,7 +830,7 @@ public static F, HashSet> Either_HashSetA() { * @return A function that converts eithers to hash sets. */ public static F, HashSet> Either_HashSetB() { - return Function.compose(Java.Option_HashSet(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_HashSet(), Conversions.Either_OptionB()); } /** @@ -870,7 +839,7 @@ public static F, HashSet> Either_HashSetB() { * @return A function that converts eithers to linked hash sets. */ public static F, LinkedHashSet> Either_LinkedHashSetA() { - return Function.compose(Java.Option_LinkedHashSet(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_LinkedHashSet(), Conversions.Either_OptionA()); } /** @@ -879,7 +848,7 @@ public static F, LinkedHashSet> Either_LinkedHashSetA() { * @return A function that converts eithers to linked hash sets. */ public static F, LinkedHashSet> Either_LinkedHashSetB() { - return Function.compose(Java.Option_LinkedHashSet(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_LinkedHashSet(), Conversions.Either_OptionB()); } /** @@ -888,7 +857,7 @@ public static F, LinkedHashSet> Either_LinkedHashSetB() { * @return A function that converts eithers to linked lists. */ public static F, LinkedList> Either_LinkedListA() { - return Function.compose(Java.Option_LinkedList(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_LinkedList(), Conversions.Either_OptionA()); } /** @@ -897,7 +866,7 @@ public static F, LinkedList> Either_LinkedListA() { * @return A function that eithers options to priority queues. */ public static F, PriorityQueue> Option_PriorityQueueA() { - return Function.compose(Java.Option_PriorityQueue(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_PriorityQueue(), Conversions.Either_OptionA()); } /** @@ -906,7 +875,7 @@ public static F, PriorityQueue> Option_PriorityQueueA() { * @return A function that eithers options to priority queues. */ public static F, PriorityQueue> Option_PriorityQueueB() { - return Function.compose(Java.Option_PriorityQueue(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_PriorityQueue(), Conversions.Either_OptionB()); } /** @@ -915,7 +884,7 @@ public static F, PriorityQueue> Option_PriorityQueueB() { * @return A function that converts eithers to linked lists. */ public static F, LinkedList> Either_LinkedListB() { - return Function.compose(Java.Option_LinkedList(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_LinkedList(), Conversions.Either_OptionB()); } /** @@ -924,7 +893,7 @@ public static F, LinkedList> Either_LinkedListB() { * @return A function that converts eithers to stacks. */ public static F, Stack> Either_StackA() { - return Function.compose(Java.Option_Stack(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_Stack(), Conversions.Either_OptionA()); } /** @@ -933,7 +902,7 @@ public static F, Stack> Either_StackA() { * @return A function that converts eithers to stacks. */ public static F, Stack> Either_StackB() { - return Function.compose(Java.Option_Stack(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_Stack(), Conversions.Either_OptionB()); } /** @@ -942,7 +911,7 @@ public static F, Stack> Either_StackB() { * @return A function that converts eithers to tree sets. */ public static F, TreeSet> Either_TreeSetA() { - return Function.compose(Java.Option_TreeSet(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_TreeSet(), Conversions.Either_OptionA()); } /** @@ -951,7 +920,7 @@ public static F, TreeSet> Either_TreeSetA() { * @return A function that converts eithers to tree sets. */ public static F, TreeSet> Either_TreeSetB() { - return Function.compose(Java.Option_TreeSet(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_TreeSet(), Conversions.Either_OptionB()); } /** @@ -960,7 +929,7 @@ public static F, TreeSet> Either_TreeSetB() { * @return A function that converts eithers to vectors. */ public static F, Vector> Either_VectorA() { - return Function.compose(Java.Option_Vector(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_Vector(), Conversions.Either_OptionA()); } /** @@ -969,7 +938,7 @@ public static F, Vector> Either_VectorA() { * @return A function that converts eithers to vectors. */ public static F, Vector> Either_VectorB() { - return Function.compose(Java.Option_Vector(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_Vector(), Conversions.Either_OptionB()); } /** @@ -979,7 +948,7 @@ public static F, Vector> Either_VectorB() { * @return A function that converts eithers to array blocking queues. */ public static F, ArrayBlockingQueue> Either_ArrayBlockingQueueA(final boolean fair) { - return Function.compose(Java.Option_ArrayBlockingQueue(fair), Conversions.Either_OptionA()); + return Function.compose(Java.Option_ArrayBlockingQueue(fair), Conversions.Either_OptionA()); } /** @@ -989,7 +958,7 @@ public static F, ArrayBlockingQueue> Either_ArrayBlocking * @return A function that converts eithers to array blocking queues. */ public static F, ArrayBlockingQueue> Either_ArrayBlockingQueueB(final boolean fair) { - return Function.compose(Java.Option_ArrayBlockingQueue(fair), Conversions.Either_OptionB()); + return Function.compose(Java.Option_ArrayBlockingQueue(fair), Conversions.Either_OptionB()); } /** @@ -998,7 +967,7 @@ public static F, ArrayBlockingQueue> Either_ArrayBlocking * @return A function that converts eithers to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Either_ConcurrentLinkedQueueA() { - return Function.compose(Java.Option_ConcurrentLinkedQueue(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_ConcurrentLinkedQueue(), Conversions.Either_OptionA()); } /** @@ -1007,7 +976,7 @@ public static F, ConcurrentLinkedQueue> Either_Concurrent * @return A function that converts eithers to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Either_ConcurrentLinkedQueueB() { - return Function.compose(Java.Option_ConcurrentLinkedQueue(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_ConcurrentLinkedQueue(), Conversions.Either_OptionB()); } /** @@ -1016,7 +985,7 @@ public static F, ConcurrentLinkedQueue> Either_Concurrent * @return A function that converts eithers to copy on write array lists. */ public static F, CopyOnWriteArrayList> Either_CopyOnWriteArrayListA() { - return Function.compose(Java.Option_CopyOnWriteArrayList(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_CopyOnWriteArrayList(), Conversions.Either_OptionA()); } /** @@ -1025,7 +994,7 @@ public static F, CopyOnWriteArrayList> Either_CopyOnWrite * @return A function that converts eithers to copy on write array lists. */ public static F, CopyOnWriteArrayList> Either_CopyOnWriteArrayListB() { - return Function.compose(Java.Option_CopyOnWriteArrayList(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_CopyOnWriteArrayList(), Conversions.Either_OptionB()); } /** @@ -1034,7 +1003,7 @@ public static F, CopyOnWriteArrayList> Either_CopyOnWrite * @return A function that converts eithers to copy on write array sets. */ public static F, CopyOnWriteArraySet> Either_CopyOnWriteArraySetA() { - return Function.compose(Java.Option_CopyOnWriteArraySet(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_CopyOnWriteArraySet(), Conversions.Either_OptionA()); } /** @@ -1043,7 +1012,7 @@ public static F, CopyOnWriteArraySet> Either_CopyOnWriteA * @return A function that converts eithers to copy on write array sets. */ public static F, CopyOnWriteArraySet> Either_CopyOnWriteArraySetB() { - return Function.compose(Java.Option_CopyOnWriteArraySet(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_CopyOnWriteArraySet(), Conversions.Either_OptionB()); } /** @@ -1052,7 +1021,7 @@ public static F, CopyOnWriteArraySet> Either_CopyOnWriteA * @return A function that converts eithers to delay queues. */ public static F, DelayQueue> Either_DelayQueueA() { - return Function.compose(Java.Option_DelayQueue(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_DelayQueue(), Conversions.Either_OptionA()); } /** @@ -1061,7 +1030,7 @@ public static F, DelayQueue> Either_Delay * @return A function that converts eithers to delay queues. */ public static F, DelayQueue> Either_DelayQueueB() { - return Function.compose(Java.Option_DelayQueue(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_DelayQueue(), Conversions.Either_OptionB()); } /** @@ -1070,7 +1039,7 @@ public static F, DelayQueue> Either_Delay * @return A function that converts eithers to linked blocking queues. */ public static F, LinkedBlockingQueue> Either_LinkedBlockingQueueA() { - return Function.compose(Java.Option_LinkedBlockingQueue(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_LinkedBlockingQueue(), Conversions.Either_OptionA()); } /** @@ -1079,7 +1048,7 @@ public static F, LinkedBlockingQueue> Either_LinkedBlocki * @return A function that converts eithers to linked blocking queues. */ public static F, LinkedBlockingQueue> Either_LinkedBlockingQueueB() { - return Function.compose(Java.Option_LinkedBlockingQueue(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_LinkedBlockingQueue(), Conversions.Either_OptionB()); } /** @@ -1088,7 +1057,7 @@ public static F, LinkedBlockingQueue> Either_LinkedBlocki * @return A function that converts eithers to priority blocking queues. */ public static F, PriorityBlockingQueue> Either_PriorityBlockingQueueA() { - return Function.compose(Java.Option_PriorityBlockingQueue(), Conversions.Either_OptionA()); + return Function.compose(Java.Option_PriorityBlockingQueue(), Conversions.Either_OptionA()); } /** @@ -1097,7 +1066,7 @@ public static F, PriorityBlockingQueue> Either_PriorityBl * @return A function that converts eithers to priority blocking queues. */ public static F, PriorityBlockingQueue> Either_PriorityBlockingQueueB() { - return Function.compose(Java.Option_PriorityBlockingQueue(), Conversions.Either_OptionB()); + return Function.compose(Java.Option_PriorityBlockingQueue(), Conversions.Either_OptionB()); } /** @@ -1107,7 +1076,7 @@ public static F, PriorityBlockingQueue> Either_PriorityBl * @return A function that converts eithers to synchronous queues. */ public static F, SynchronousQueue> Either_SynchronousQueueA(final boolean fair) { - return Function.compose(Java.Option_SynchronousQueue(fair), Conversions.Either_OptionA()); + return Function.compose(Java.Option_SynchronousQueue(fair), Conversions.Either_OptionA()); } /** @@ -1117,7 +1086,7 @@ public static F, SynchronousQueue> Either_SynchronousQueu * @return A function that converts eithers to synchronous queues. */ public static F, SynchronousQueue> Either_SynchronousQueueB(final boolean fair) { - return Function.compose(Java.Option_SynchronousQueue(fair), Conversions.Either_OptionB()); + return Function.compose(Java.Option_SynchronousQueue(fair), Conversions.Either_OptionB()); } // END Either -> @@ -1128,49 +1097,49 @@ public static F, SynchronousQueue> Either_SynchronousQueu * A function that converts strings to array lists. */ public static final F> String_ArrayList = - Function.compose(Java.List_ArrayList(), Conversions.String_List); + Function.compose(Java.List_ArrayList(), Conversions.String_List); /** * A function that converts strings to hash sets. */ public static final F> String_HashSet = - Function.compose(Java.List_HashSet(), Conversions.String_List); + Function.compose(Java.List_HashSet(), Conversions.String_List); /** * A function that converts strings to linked hash sets. */ public static final F> String_LinkedHashSet = - Function.compose(Java.List_LinkedHashSet(), Conversions.String_List); + Function.compose(Java.List_LinkedHashSet(), Conversions.String_List); /** * A function that converts strings to linked lists. */ public static final F> String_LinkedList = - Function.compose(Java.List_LinkedList(), Conversions.String_List); + Function.compose(Java.List_LinkedList(), Conversions.String_List); /** * A function that converts strings to priority queues. */ public static final F> String_PriorityQueue = - Function.compose(Java.List_PriorityQueue(), Conversions.String_List); + Function.compose(Java.List_PriorityQueue(), Conversions.String_List); /** * A function that converts strings to stacks. */ public static final F> String_Stack = - Function.compose(Java.List_Stack(), Conversions.String_List); + Function.compose(Java.List_Stack(), Conversions.String_List); /** * A function that converts strings to tree sets. */ public static final F> String_TreeSet = - Function.compose(Java.List_TreeSet(), Conversions.String_List); + Function.compose(Java.List_TreeSet(), Conversions.String_List); /** * A function that converts strings to vectors. */ public static final F> String_Vector = - Function.compose(Java.List_Vector(), Conversions.String_List); + Function.compose(Java.List_Vector(), Conversions.String_List); /** * A function that converts strings to array blocking queues. @@ -1179,38 +1148,38 @@ public static F, SynchronousQueue> Either_SynchronousQueu * @return A function that converts strings to array blocking queues. */ public static F> String_ArrayBlockingQueue(final boolean fair) { - return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.String_List); + return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.String_List); } /** * A function that converts strings to concurrent linked queues. */ public static final F> String_ConcurrentLinkedQueue = - Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.String_List); + Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.String_List); /** * A function that converts strings to copy on write array lists. */ public static final F> String_CopyOnWriteArrayList = - Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.String_List); + Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.String_List); /** * A function that converts strings to copy on write array sets. */ public static final F> String_CopyOnWriteArraySet = - Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.String_List); + Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.String_List); /** * A function that converts strings to linked blocking queues. */ public static final F> String_LinkedBlockingQueue = - Function.compose(Java.List_LinkedBlockingQueue(), Conversions.String_List); + Function.compose(Java.List_LinkedBlockingQueue(), Conversions.String_List); /** * A function that converts strings to priority blocking queues. */ public static final F> String_PriorityBlockingQueue = - Function.compose(Java.List_PriorityBlockingQueue(), Conversions.String_List); + Function.compose(Java.List_PriorityBlockingQueue(), Conversions.String_List); /** * A function that converts strings to synchronous queues. @@ -1219,7 +1188,7 @@ public static F> String_ArrayBlockingQueue * @return A function that converts strings to synchronous queues. */ public static F> String_SynchronousQueue(final boolean fair) { - return Function.compose(Java.List_SynchronousQueue(fair), Conversions.String_List); + return Function.compose(Java.List_SynchronousQueue(fair), Conversions.String_List); } // END String -> @@ -1230,49 +1199,49 @@ public static F> String_SynchronousQueue(fin * A function that converts string buffers to array lists. */ public static final F> StringBuffer_ArrayList = - Function.compose(Java.List_ArrayList(), Conversions.StringBuffer_List); + Function.compose(Java.List_ArrayList(), Conversions.StringBuffer_List); /** * A function that converts string buffers to hash sets. */ public static final F> StringBuffer_HashSet = - Function.compose(Java.List_HashSet(), Conversions.StringBuffer_List); + Function.compose(Java.List_HashSet(), Conversions.StringBuffer_List); /** * A function that converts string buffers to linked hash sets. */ public static final F> StringBuffer_LinkedHashSet = - Function.compose(Java.List_LinkedHashSet(), Conversions.StringBuffer_List); + Function.compose(Java.List_LinkedHashSet(), Conversions.StringBuffer_List); /** * A function that converts string buffers to linked lists. */ public static final F> StringBuffer_LinkedList = - Function.compose(Java.List_LinkedList(), Conversions.StringBuffer_List); + Function.compose(Java.List_LinkedList(), Conversions.StringBuffer_List); /** * A function that converts string buffers to priority queues. */ public static final F> StringBuffer_PriorityQueue = - Function.compose(Java.List_PriorityQueue(), Conversions.StringBuffer_List); + Function.compose(Java.List_PriorityQueue(), Conversions.StringBuffer_List); /** * A function that converts string buffers to stacks. */ public static final F> StringBuffer_Stack = - Function.compose(Java.List_Stack(), Conversions.StringBuffer_List); + Function.compose(Java.List_Stack(), Conversions.StringBuffer_List); /** * A function that converts string buffers to tree sets. */ public static final F> StringBuffer_TreeSet = - Function.compose(Java.List_TreeSet(), Conversions.StringBuffer_List); + Function.compose(Java.List_TreeSet(), Conversions.StringBuffer_List); /** * A function that converts string buffers to vectors. */ public static final F> StringBuffer_Vector = - Function.compose(Java.List_Vector(), Conversions.StringBuffer_List); + Function.compose(Java.List_Vector(), Conversions.StringBuffer_List); /** * A function that converts string buffers to array blocking queues. @@ -1281,38 +1250,38 @@ public static F> String_SynchronousQueue(fin * @return A function that converts string buffers to array blocking queues. */ public static F> StringBuffer_ArrayBlockingQueue(final boolean fair) { - return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.StringBuffer_List); + return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.StringBuffer_List); } /** * A function that converts string buffers to concurrent linked queues. */ public static final F> StringBuffer_ConcurrentLinkedQueue = - Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.StringBuffer_List); + Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.StringBuffer_List); /** * A function that converts string buffers to copy on write array lists. */ public static final F> StringBuffer_CopyOnWriteArrayList = - Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.StringBuffer_List); + Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.StringBuffer_List); /** * A function that converts string buffers to copy on write array sets. */ public static final F> StringBuffer_CopyOnWriteArraySet = - Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.StringBuffer_List); + Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.StringBuffer_List); /** * A function that converts string buffers to linked blocking queues. */ public static final F> StringBuffer_LinkedBlockingQueue = - Function.compose(Java.List_LinkedBlockingQueue(), Conversions.StringBuffer_List); + Function.compose(Java.List_LinkedBlockingQueue(), Conversions.StringBuffer_List); /** * A function that converts string buffers to priority blocking queues. */ public static final F> StringBuffer_PriorityBlockingQueue = - Function.compose(Java.List_PriorityBlockingQueue(), Conversions.StringBuffer_List); + Function.compose(Java.List_PriorityBlockingQueue(), Conversions.StringBuffer_List); /** * A function that converts string buffers to synchronous queues. @@ -1321,7 +1290,7 @@ public static F> StringBuffer_ArrayB * @return A function that converts string buffers to synchronous queues. */ public static F> StringBuffer_SynchronousQueue(final boolean fair) { - return Function.compose(Java.List_SynchronousQueue(fair), Conversions.StringBuffer_List); + return Function.compose(Java.List_SynchronousQueue(fair), Conversions.StringBuffer_List); } // END StringBuffer -> @@ -1332,49 +1301,49 @@ public static F> StringBuffer_Synchron * A function that converts string builders to array lists. */ public static final F> StringBuilder_ArrayList = - Function.compose(Java.List_ArrayList(), Conversions.StringBuilder_List); + Function.compose(Java.List_ArrayList(), Conversions.StringBuilder_List); /** * A function that converts string builders to hash sets. */ public static final F> StringBuilder_HashSet = - Function.compose(Java.List_HashSet(), Conversions.StringBuilder_List); + Function.compose(Java.List_HashSet(), Conversions.StringBuilder_List); /** * A function that converts string builders to linked hash sets. */ public static final F> StringBuilder_LinkedHashSet = - Function.compose(Java.List_LinkedHashSet(), Conversions.StringBuilder_List); + Function.compose(Java.List_LinkedHashSet(), Conversions.StringBuilder_List); /** * A function that converts string builders to linked lists. */ public static final F> StringBuilder_LinkedList = - Function.compose(Java.List_LinkedList(), Conversions.StringBuilder_List); + Function.compose(Java.List_LinkedList(), Conversions.StringBuilder_List); /** * A function that converts string builders to priority queues. */ public static final F> StringBuilder_PriorityQueue = - Function.compose(Java.List_PriorityQueue(), Conversions.StringBuilder_List); + Function.compose(Java.List_PriorityQueue(), Conversions.StringBuilder_List); /** * A function that converts string builders to stacks. */ public static final F> StringBuilder_Stack = - Function.compose(Java.List_Stack(), Conversions.StringBuilder_List); + Function.compose(Java.List_Stack(), Conversions.StringBuilder_List); /** * A function that converts string builders to tree sets. */ public static final F> StringBuilder_TreeSet = - Function.compose(Java.List_TreeSet(), Conversions.StringBuilder_List); + Function.compose(Java.List_TreeSet(), Conversions.StringBuilder_List); /** * A function that converts string builders to vectors. */ public static final F> StringBuilder_Vector = - Function.compose(Java.List_Vector(), Conversions.StringBuilder_List); + Function.compose(Java.List_Vector(), Conversions.StringBuilder_List); /** * A function that converts string builders to array blocking queues. @@ -1383,38 +1352,38 @@ public static F> StringBuffer_Synchron * @return A function that converts string builders to array blocking queues. */ public static F> StringBuilder_ArrayBlockingQueue(final boolean fair) { - return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.StringBuilder_List); + return Function.compose(Java.List_ArrayBlockingQueue(fair), Conversions.StringBuilder_List); } /** * A function that converts string builders to concurrent linked queues. */ public static final F> StringBuilder_ConcurrentLinkedQueue = - Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.StringBuilder_List); + Function.compose(Java.List_ConcurrentLinkedQueue(), Conversions.StringBuilder_List); /** * A function that converts string builders to copy on write array lists. */ public static final F> StringBuilder_CopyOnWriteArrayList = - Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.StringBuilder_List); + Function.compose(Java.List_CopyOnWriteArrayList(), Conversions.StringBuilder_List); /** * A function that converts string builders to copy on write array sets. */ public static final F> StringBuilder_CopyOnWriteArraySet = - Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.StringBuilder_List); + Function.compose(Java.List_CopyOnWriteArraySet(), Conversions.StringBuilder_List); /** * A function that converts string builders to linked blocking queues. */ public static final F> StringBuilder_LinkedBlockingQueue = - Function.compose(Java.List_LinkedBlockingQueue(), Conversions.StringBuilder_List); + Function.compose(Java.List_LinkedBlockingQueue(), Conversions.StringBuilder_List); /** * A function that converts string builders to priority blocking queues. */ public static final F> StringBuilder_PriorityBlockingQueue = - Function.compose(Java.List_PriorityBlockingQueue(), Conversions.StringBuilder_List); + Function.compose(Java.List_PriorityBlockingQueue(), Conversions.StringBuilder_List); /** * A function that converts string builders to synchronous queues. @@ -1423,7 +1392,7 @@ public static F> StringBuilder_Arra * @return A function that converts string builders to synchronous queues. */ public static F> StringBuilder_SynchronousQueue(final boolean fair) { - return Function.compose(Java.List_SynchronousQueue(fair), Conversions.StringBuilder_List); + return Function.compose(Java.List_SynchronousQueue(fair), Conversions.StringBuilder_List); } // END StringBuffer -> @@ -1436,25 +1405,15 @@ public static F> StringBuilder_Synchr * @return A function that converts array lists to lists. */ public static F, List> ArrayList_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo // 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. - */ - public static F, List> JUList_List() { - return as -> JavaList_List(as); - } - public static F, List> JavaList_List() { - return as -> JavaList_List(as); + return Java::JavaList_List; } public static List JavaList_List(java.util.List list) { @@ -1467,7 +1426,7 @@ public static List JavaList_List(java.util.List list) { * A function that converts bit sets to lists. */ public static final F> BitSet_List = s -> List.unfold(i -> i == s.length() ? - Option.>none() : + Option.none() : some(p(s.get(i), i + 1)), 0); // todo @@ -1482,7 +1441,7 @@ public static List JavaList_List(java.util.List list) { * @return A function that converts enum sets to lists. */ public static > F, List> EnumSet_List() { - return as -> Collection_List(as); + return Java::Collection_List; } public static List Collection_List(Collection c) { @@ -1490,7 +1449,7 @@ public static List Collection_List(Collection c) { } public static F, List> Collection_List() { - return c -> List.list(c.toArray(array(c.size()))); + return c -> List.list(c.toArray(array(c.size()))); } @SafeVarargs @@ -1510,7 +1469,7 @@ private static E[] array(int length, E... array) { * @return A function that converts hash sets to lists. */ public static F, List> HashSet_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1525,7 +1484,7 @@ public static F, List> HashSet_List() { * @return A function that converts linked hash sets to lists. */ public static F, List> LinkedHashSet_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1540,7 +1499,7 @@ public static F, List> LinkedHashSet_List() { * @return A function that converts linked lists to lists. */ public static F, List> LinkedList_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1555,7 +1514,7 @@ public static F, List> LinkedList_List() { * @return A function that converts priority queues to lists. */ public static F, List> PriorityQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1570,7 +1529,7 @@ public static F, List> PriorityQueue_List() { * @return A function that converts stacks to lists. */ public static F, List> Stack_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1585,7 +1544,7 @@ public static F, List> Stack_List() { * @return A function that converts tree sets to lists. */ public static F, List> TreeSet_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1600,7 +1559,7 @@ public static F, List> TreeSet_List() { * @return A function that converts vectors to lists. */ public static F, List> Vector_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1615,7 +1574,7 @@ public static F, List> Vector_List() { * @return A function that converts array blocking queues to lists. */ public static F, List> ArrayBlockingQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1630,7 +1589,7 @@ public static F, List> ArrayBlockingQueue_List() { * @return A function that converts concurrent linked queues to lists. */ public static F, List> ConcurrentLinkedQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1645,7 +1604,7 @@ public static F, List> ConcurrentLinkedQueue_Lis * @return A function that converts copy on write array lists to lists. */ public static F, List> CopyOnWriteArrayList_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1660,7 +1619,7 @@ public static F, List> CopyOnWriteArrayList_List( * @return A function that converts copy on write array sets to lists. */ public static F, List> CopyOnWriteArraySet_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1675,7 +1634,7 @@ public static F, List> CopyOnWriteArraySet_List() * @return A function that converts delay queues to lists. */ public static F, List> DelayQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1690,7 +1649,7 @@ public static F, List> DelayQueue_List() { * @return A function that converts linked blocking queues to lists. */ public static F, List> LinkedBlockingQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1705,7 +1664,7 @@ public static F, List> LinkedBlockingQueue_List() * @return A function that converts priority blocking queues to lists. */ public static F, List> PriorityBlockingQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1720,7 +1679,7 @@ public static F, List> PriorityBlockingQueue_Lis * @return A function that converts synchronous queues to lists. */ public static F, List> SynchronousQueue_List() { - return as -> Collection_List(as); + return Java::Collection_List; } // todo @@ -1730,7 +1689,7 @@ public static F, List> SynchronousQueue_List() { // BEGIN Callable -> public static F, Callable> P1_Callable() { - return a -> () -> a._1(); + return a -> a::_1; } // END Callable -> diff --git a/java8/src/main/java/fj/data/Java8.java b/core/src/main/java/fj/data/Java8.java similarity index 50% rename from java8/src/main/java/fj/data/Java8.java rename to core/src/main/java/fj/data/Java8.java index 03e310d2..184c01c5 100644 --- a/java8/src/main/java/fj/data/Java8.java +++ b/core/src/main/java/fj/data/Java8.java @@ -1,13 +1,5 @@ package fj.data; -import java.util.Iterator; -import java.util.Optional; -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; @@ -18,9 +10,15 @@ import fj.function.Try1; import fj.function.Try2; -/** - * Created by mperry on 3/06/2014. - */ +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; + public final class Java8 { private Java8() { @@ -28,99 +26,102 @@ private Java8() { } public static P1 Supplier_P1(final Supplier s) { - return Java8.Supplier_P1().f(s); + return P.lazy(s::get); } public static F, P1> Supplier_P1() { - return s -> P.lazy(() -> s.get()); + return Java8::Supplier_P1; } public static Supplier P1_Supplier(final P1 p) { - return Java8.P1_Supplier().f(p); + return p::_1; } public static F, Supplier> P1_Supplier() { - return (p) -> () -> p._1(); + return Java8::P1_Supplier; } public static F Function_F(final Function f) { - return Java8.Function_F().f(f); + return f::apply; } public static F, F> Function_F() { - return f -> a -> f.apply(a); + return Java8::Function_F; } public static Function F_Function(final F f) { - return Java8.F_Function().f(f); + return f::f; } public static F, Function> F_Function() { - return f -> a -> f.f(a); + return Java8::F_Function; } public static F2 BiFunction_F2(final BiFunction f) { - return Java8.BiFunction_F2().f(f); + return f::apply; } public static F, F2> BiFunction_F2() { - return f -> (a, b) -> f.apply(a, b); + return Java8::BiFunction_F2; } public static BiFunction F2_BiFunction(final F2 f) { - return Java8.F2_BiFunction().f(f); + return f::f; } public static F, BiFunction> F2_BiFunction() { - return f -> (a, b) -> f.f(a, b); + return Java8::F2_BiFunction; } - static public Supplier> TryCatch0_Supplier(final Try0 t) { - return Java8.TryCatch0_Supplier().f(t); + public static Supplier> TryCatch0_Supplier(final Try0 t) { + return () -> Try.f(t)._1(); } - static public F, Supplier>> TryCatch0_Supplier() { - return t -> () -> Try.f(t)._1(); + public static F, Supplier>> TryCatch0_Supplier() { + return Java8::TryCatch0_Supplier; } - static public Function> TryCatch1_Function(final Try1 t) { - return Java8.TryCatch1_Function().f(t); + public static Function> TryCatch1_Function(final Try1 t) { + return a -> Try.f(t).f(a); } - static public F, Function>> TryCatch1_Function() { - return t -> a -> Try.f(t).f(a); + public static F, Function>> TryCatch1_Function() { + return Java8::TryCatch1_Function; } - static public BiFunction> TryCatch2_BiFunction(final Try2 t) { - return Java8.TryCatch2_BiFunction().f(t); + public static BiFunction> TryCatch2_BiFunction(final Try2 t) { + return (a, b) -> Try.f(t).f(a, b); } - static public F, BiFunction>> TryCatch2_BiFunction() { - return t -> (a, b) -> Try.f(t).f(a, b); + public static F, BiFunction>> TryCatch2_BiFunction() { + return Java8::TryCatch2_BiFunction; } public static java.util.stream.Stream List_JavaStream(final List list) { return Iterable_JavaStream(list); } - static public Option Optional_Option(final Optional o) { - return Java8.Optional_Option().f(o); + public static Option Optional_Option(final Optional o) { + return o.isPresent() ? Option.some(o.get()) : Option.none(); } - static public F, Option> Optional_Option() { - return o -> o.isPresent() ? Option.fromNull(o.get()) : Option.none(); + public static F, Option> Optional_Option() { + return Java8::Optional_Option; } - static public Optional Option_Optional(final Option o) { - return Java8.Option_Optional().f(o); + /** + * Convert an Option to {@link Optional}. Will throw a {@link NullPointerException} if the Option is some(null). + */ + public static Optional Option_Optional(final Option o) { + return o.option(Optional.empty(), Optional::of); } - static public F, Optional> Option_Optional() { - return o -> o.isSome() ? Optional.ofNullable(o.some()) : Optional.empty(); + public static F, Optional> Option_Optional() { + return Java8::Option_Optional; } - static public F, F> Consumer_F() { - return c -> Consumer_F(c); + public static F, F> Consumer_F() { + return Java8::Consumer_F; } public static F Consumer_F(final Consumer c) { @@ -130,31 +131,31 @@ public static F Consumer_F(final Consumer c) { }; } - static public 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); } - static public java.util.stream.Stream Iterable_JavaStream(final Iterable it) { + public static java.util.stream.Stream Iterable_JavaStream(final Iterable it) { return StreamSupport.stream(it.spliterator(), false); } - static public java.util.stream.Stream Iterator_JavaStream(final Iterator it) { - return Iterable_JavaStream(() -> it); + public static java.util.stream.Stream Iterator_JavaStream(final Iterator it) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false); } - static public F, java.util.stream.Stream> Stream_JavaStream() { - return s -> Stream_JavaStream(s); + public static F, java.util.stream.Stream> Stream_JavaStream() { + return Java8::Stream_JavaStream; } - static public Stream JavaStream_Stream(final java.util.stream.Stream s) { - return s.collect(Collectors.toStream()); + public static Stream JavaStream_Stream(final java.util.stream.Stream s) { + return Stream.iteratorStream(s.iterator()); } - static public List JavaStream_List(final java.util.stream.Stream s) { + public static List JavaStream_List(final java.util.stream.Stream s) { return s.collect(Collectors.toList()); } - static public Array JavaStream_Array(final java.util.stream.Stream s) { + public static Array JavaStream_Array(final java.util.stream.Stream s) { return s.collect(Collectors.toArray()); } diff --git a/core/src/main/java/fj/data/LazyString.java b/core/src/main/java/fj/data/LazyString.java index 95160de1..3e393cf8 100644 --- a/core/src/main/java/fj/data/LazyString.java +++ b/core/src/main/java/fj/data/LazyString.java @@ -224,7 +224,7 @@ public LazyString reverse() { * @return The first index of the given character in this lazy string, or None if the character is not present. */ public Option indexOf(final char c) { - return s.indexOf(Equal.charEqual.eq(c)); + return s.indexOf(charEqual.eq(c)); } /** @@ -256,7 +256,7 @@ public boolean matches(final String regex) { public Stream split(final F p) { final Stream findIt = s.dropWhile(p); final P2, Stream> ws = findIt.split(p); - return findIt.isEmpty() ? Stream.nil() + return findIt.isEmpty() ? Stream.nil() : Stream.cons(fromStream(ws._1()), () -> fromStream(ws._2()).split(p)); } diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index 2d6f7683..721ee9f1 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -1,58 +1,33 @@ 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.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. - * - * @version %build.number% */ public abstract class List implements Iterable { private List() { @@ -88,10 +63,7 @@ public final Iterator iterator() { * @return The length of this list. */ public final int length() { - // WARNING: In JDK 8, update 25 (current version) the following code triggers an internal JDK compiler error, likely due to https://bugs.openjdk.java.net/browse/JDK-8062253. The code below is a workaround for this compiler bug. - // return foldLeft(i -> a -> i + 1, 0); - F2 f = (i, a) -> i + 1; - return foldLeft(f, 0); + return foldLeft((i, a) -> i + 1, 0); } /** @@ -112,19 +84,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(Function.uncurryF2(cons), nil); - } - public final B uncons(final F2, B> cons, final B nil) { return isEmpty() ? nil : cons.f(head(), tail()); } @@ -150,24 +109,12 @@ public final List orTail(final F0> as) { } /** - * 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()} . + * Returns the head of the list, if any. * * @return The optional head of the list. */ - public Option headOption() { - return isEmpty() ? Option.none() : some(head()); + public final Option headOption() { + return isEmpty() ? Option.none() : some(head()); } /** @@ -178,7 +125,7 @@ public Option headOption() { * @return An either projection of this list. */ public final Either toEither(final F0 x) { - return isEmpty() ? Either.left(x.f()) : Either.right(head()); + return isEmpty() ? Either.left(x.f()) : Either.right(head()); } /** @@ -195,7 +142,7 @@ public final Stream toStream() { * * @return A array projection of this list. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final Array toArray() { return mkArray(toArrayObject()); } @@ -211,11 +158,6 @@ public final Object[] toArrayObject() { return a; } - @SuppressWarnings("unchecked") - public final A[] toJavaArray() { - return (A[]) toArrayObject(); - } - /** * Returns a array projection of this list. * @@ -251,7 +193,7 @@ public final A[] array(final Class c) { * @return A new list with the given element at the head. */ public final List cons(final A a) { - return new Cons(a, this); + return new Cons<>(a, this); } /** @@ -262,7 +204,7 @@ public final List cons(final A a) { * @return A new list with the given element at the head. */ public final List conss(final A a) { - return new Cons(a, this); + return new Cons<>(a, this); } /** @@ -403,9 +345,9 @@ public final P2, List> span(final F p) { if (p.f(xs.head())) b.snoc(xs.head()); else - return P.p(b.toList(), xs); + return p(b.toList(), xs); } - return P.p(b.toList(), List.nil()); + return p(b.toList(), List.nil()); } /** @@ -598,89 +540,344 @@ 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 Option> traverseOption(final F> f) { - return foldRight( - (a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))), - Option.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 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 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 P1> traverseP1(final F> f){ - return foldRight( - (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), - P.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 IO> traverseIO(F> f) { - return this.foldRight( - (a, acc) -> IOFunctions.bind(acc, (bs) -> IOFunctions.map(f.f(a), b -> 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()); + } - public F> traverseF(F> f) { + /** + * 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(b -> bs.cons(b), f.f(a))), - Function.constant(List. nil()) - ); + (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()) + ); } - public Trampoline> traverseTrampoline(final F> f) { + /** + * 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))), - Trampoline.pure(List. nil())); + single(List.nil())); } - public Promise> traversePromise(final F> f) { + /** + * 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, acc) -> f.f(a).bind(b -> acc.fmap(bs -> bs.cons(b))), - Promise.promise(Strategy.idStrategy(), p(List. nil()))); + (a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))), + some(List.nil()) + ); } - public List> traverseList(final F> f) { + /** + * 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))), - List.single(List. nil())); + p(List.nil()) + ); } - public Validation> traverseValidation(final F> f) { + /** + * 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))), - Validation.success(List. nil())); + Trampoline.pure(List.nil())); + } + + public final Promise> traversePromise(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.fmap(bs -> bs.cons(b))), + Promise.promise(Strategy.idStrategy(), p(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)); } - public V2> traverseV2(final F> f) { + public final V2> traverseV2(final F> f) { return foldRight( (a, acc) -> acc.apply(f.f(a)., List>> map(e -> es -> es.cons(e))), - v(List. nil(), List. nil())); + v(List.nil(), List.nil())); } /** @@ -690,7 +887,7 @@ public V2> traverseV2(final F> f) { * @return A new list after applying the given list of functions through this list. */ public final List apply(final List> lf) { - return lf.bind(f -> map(f)); + return lf.bind(this::map); } /** @@ -700,7 +897,7 @@ public final List apply(final List> lf) { * @return A new list that has appended the given list. */ public final List append(final List as) { - return Buffer.fromList(this).prependToList(as); + return fromList(this).prependToList(as); } /** @@ -711,7 +908,7 @@ public final List append(final List as) { * @return The final result after the right-fold reduction. */ public final B foldRight(final F> f, final B b) { - return reverse().foldLeft(flip(f), b); + return foldRight(uncurryF2(f), b); } /** @@ -722,7 +919,7 @@ public final B foldRight(final F> f, final B b) { * @return The final result after the right-fold reduction. */ public final B foldRight(final F2 f, final B b) { - return foldRight(curry(f), b); + return reverse().foldLeft(flip(f), b); } /** @@ -732,7 +929,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(f.f(head()))); } /** @@ -743,13 +940,7 @@ public final Trampoline foldRightC(final F2 f, final B b) { * @return The final result after the left-fold reduction. */ public final B foldLeft(final F> f, final B b) { - B x = b; - - for (List xs = this; !xs.isEmpty(); xs = xs.tail()) { - x = f.f(x).f(xs.head()); - } - - return x; + return foldLeft(uncurryF2(f), b); } /** @@ -760,7 +951,13 @@ public final B foldLeft(final F> f, final B b) { * @return The final result after the left-fold reduction. */ public final B foldLeft(final F2 f, final B b) { - return foldLeft(curry(f), b); + B x = b; + + for (List xs = this; !xs.isEmpty(); xs = xs.tail()) { + x = f.f(x, xs.head()); + } + + return x; } /** @@ -771,7 +968,9 @@ public final B foldLeft(final F2 f, final B b) { * @return The final result after the left-fold reduction. */ public final A foldLeft1(final F2 f) { - return foldLeft1(curry(f)); + if (isEmpty()) + throw error("Undefined: foldLeft1 on empty list"); + return tail().foldLeft(f, head()); } /** @@ -782,9 +981,7 @@ public final A foldLeft1(final F2 f) { * @return The final result after the left-fold reduction. */ public final A foldLeft1(final F> f) { - if (isEmpty()) - throw error("Undefined: foldLeft1 on empty list"); - return tail().foldLeft(f, head()); + return foldLeft1(uncurryF2(f)); } /** @@ -793,7 +990,7 @@ public final A foldLeft1(final F> f) { * @return A new list that is the reverse of this one. */ public final List reverse() { - return foldLeft(as -> a -> cons(a, as), List.nil()); + return foldLeft((as, a) -> cons(a, as), nil()); } /** @@ -823,7 +1020,7 @@ public final A index(final int i) { * @return A new list with a length the same, or less than, this list. */ public final List take(final int i) { - Buffer result = Buffer.empty(); + Buffer result = empty(); List list = this; int index = i; while (index > 0 && list.isNotEmpty()) { @@ -841,11 +1038,9 @@ public final List take(final int i) { * @return A list with a length the same, or less than, this list. */ public final List drop(final int i) { - int c = 0; - List xs = this; - for (; xs.isNotEmpty() && c < i; xs = xs.tail()) + for (int c = 0; xs.isNotEmpty() && c < i; xs = xs.tail()) c++; return xs; @@ -860,8 +1055,8 @@ public final List drop(final int i) { */ public final P2, List> splitAt(final int i) { int c = 0; - List first = List.nil(); - List second = List.nil(); + List first = List.nil(); + List second = nil(); for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) { final A h = xs.head(); if (c < i) { @@ -871,7 +1066,7 @@ public final P2, List> splitAt(final int i) { } c++; } - return P.p(first.reverse(), second.reverse()); + return p(first.reverse(), second.reverse()); } /** @@ -886,7 +1081,7 @@ public final List> partition(final int n) { throw error("Can't create list partitions shorter than 1 element long."); if (isEmpty()) throw error("Partition on empty list."); - return unfold(as -> as.isEmpty() ? Option., List>>none() : some(as.splitAt(n)), this); + return unfold(as -> as.isEmpty() ? Option.none() : some(as.splitAt(n)), this); } /** @@ -897,12 +1092,12 @@ public final List> partition(final int n) { * * @param f Predicate function. */ - public P2, List> partition(F f) { - P2, List> p2 = foldLeft(acc -> a -> - f.f(a) ? P.p(acc._1().cons(a), acc._2()) : P.p(acc._1(), acc._2().cons(a)), - P.p(nil(), nil()) + public final P2, List> partition(F f) { + P2, List> p2 = foldLeft((acc, a) -> + f.f(a) ? p(acc._1().cons(a), acc._2()) : p(acc._1(), acc._2().cons(a)), + p(nil(), nil()) ); - return P.p(p2._1().reverse(), p2._2().reverse()); + return p(p2._1().reverse(), p2._2().reverse()); } /** @@ -911,7 +1106,7 @@ public P2, List> partition(F f) { * @return The list of initial segments of this list, shortest first. */ public final List> inits() { - List> s = single(List.nil()); + List> s = single(List.nil()); if (isNotEmpty()) s = s.append(tail().inits().map(List.cons().f(head()))); return s; @@ -923,7 +1118,7 @@ public final List> inits() { * @return The list of final segments of this list, longest first. */ public final List> tails() { - return isEmpty() ? single(List.nil()) : cons(this, tail().tails()); + return isEmpty() ? single(List.nil()) : cons(this, tail().tails()); } /** @@ -938,7 +1133,7 @@ public final List sort(final Ord o) { else if (tail().isEmpty()) return this; else { - final class Merge { + final class Merge { List merge(List xs, List ys, final Ord o) { final Buffer buf = empty(); @@ -956,7 +1151,7 @@ List merge(List xs, List ys, final Ord o) { final A x = xs.head(); final A y = ys.head(); - if (o.isLessThan(x, y)) { + if (o.isLessThanOrEqualTo(x, y)) { buf.snoc(x); xs = xs.tail(); } else { @@ -970,7 +1165,7 @@ List merge(List xs, List ys, final Ord o) { } final P2, List> s = splitAt(length() / 2); - return new Merge().merge(s._1().sort(o), s._2().sort(o), o); + return new Merge().merge(s._1().sort(o), s._2().sort(o), o); } } @@ -1036,7 +1231,7 @@ public final List> zip(final List bs) { * @return A function that zips the given lists to produce a list of pairs. */ public static F, F, List>>> zip() { - return curry((as, bs) -> as.zip(bs)); + return curry(List::zip); } /** @@ -1108,7 +1303,7 @@ public final Option find(final F f) { public final List intersperse(final A a) { return isEmpty() || tail().isEmpty() ? this : - cons(head(), tail().bind(a2 -> List.list(a, a2))); + cons(head(), tail().bind(a2 -> list(a, a2))); } /** @@ -1117,7 +1312,7 @@ public final List intersperse(final A a) { * @param as The list to intersperse through. * @return This list through the given list then joins the results. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final List intercalate(final List> as) { return join(as.intersperse(this)); } @@ -1128,7 +1323,7 @@ public final List intercalate(final List> as) { * @return A list without duplicates according to object equality. */ public final List nub() { - return nub(Equal.anyEqual()); + return nub(Equal.anyEqual()); } /** @@ -1147,9 +1342,9 @@ public final List nub(final Equal eq) { * @param o An ordering for the elements. * @return A list without duplicates. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final List nub(final Ord o) { - return sort(o).group(o.equal()).map(List.head_()); + return sort(o).group(o.equal()).map(List.head_()); } @@ -1159,7 +1354,7 @@ public final List nub(final Ord o) { * @return A function that gets the head of a given list. */ public static F, A> head_() { - return list -> list.head(); + return List::head; } @@ -1167,7 +1362,7 @@ public static F, A> head_() { * Reutrns the tail of the list, if any. * @return The optional tail of the list. */ - public Option> tailOption() { + public final Option> tailOption() { return isEmpty() ? none() : some(tail()); } @@ -1177,7 +1372,7 @@ public Option> tailOption() { * @return A function that gets the tail of a given list. */ public static F, List> tail_() { - return list -> list.tail(); + return List::tail; } /** @@ -1222,7 +1417,7 @@ public final Option> mapMOption(final F> f) { * @return A list of values in the Trampoline monad. */ public final Trampoline> mapMTrampoline(final F> f) { - return foldRight((a, bs) -> f.f(a).bind(b -> bs.map(bbs -> bbs.cons(b))), Trampoline.>pure(List.nil())); + return foldRight((a, bs) -> f.f(a).bind(b -> bs.map(bbs -> bbs.cons(b))), Trampoline.pure(List.nil())); } /** @@ -1291,18 +1486,7 @@ public final List insertBy(final F> f, final A x) { * @return The most common element in this list. */ 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()}. - * - * @param keyFunction The function to select the keys for the map. - * @return A TreeMap containing the keys with the accumulated list of matched elements. - */ - public final TreeMap> groupBy(final F keyFunction) { - return groupBy(keyFunction, Ord.hashOrd()); + return sort(o).group(o.equal()).maximum(intOrd.contramap(List.length_())).head(); } /** @@ -1313,22 +1497,7 @@ public final TreeMap> groupBy(final F keyFunction) { * @return A TreeMap containing the keys with the accumulated list of matched elements. */ public final TreeMap> groupBy(final F keyFunction, final Ord keyOrd) { - return groupBy(keyFunction, Function.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()}. - * - * @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. - */ - public final TreeMap> groupBy( - final F keyFunction, - final F valueFunction) { - return this.groupBy(keyFunction, valueFunction, Ord.hashOrd()); + return groupBy(keyFunction, identity(), keyOrd); } /** @@ -1345,7 +1514,7 @@ public final TreeMap> groupBy( final F keyFunction, final F valueFunction, final Ord keyOrd) { - return this.>groupBy(keyFunction, valueFunction, List.nil(), List::cons, keyOrd); + return this.groupBy(keyFunction, valueFunction, List.nil(), List::cons, keyOrd); } /** @@ -1365,7 +1534,7 @@ public final TreeMap groupBy( final Monoid monoid, final Ord keyOrd) { return groupBy(keyFunction, valueFunction, monoid.zero(), - Function.uncurryF2(monoid.sum()), keyOrd); + uncurryF2(monoid.sum()), keyOrd); } /** @@ -1387,14 +1556,15 @@ public final TreeMap groupBy( final D groupingIdentity, final F2 groupingAcc, final Ord keyOrd) { - return this.foldLeft(map -> element -> { - final B key = keyFunction.f(element); - final C value = valueFunction.f(element); - return map.set(key, map.get(key) - .map(existing -> groupingAcc.f(value, existing)) - .orSome(groupingAcc.f(value, groupingIdentity))); - }, TreeMap.empty(keyOrd) - ); + java.util.TreeMap buffer = new java.util.TreeMap<>(keyOrd.toComparator()); + foreachDoEffect(element -> { + final B key = keyFunction.f(element); + final C value = valueFunction.f(element); + buffer.put(key, buffer.containsKey(key) + ? groupingAcc.f(value, buffer.get(key)) + : groupingAcc.f(value, groupingIdentity)); + }); + return TreeMap.fromMutableMap(keyOrd, buffer); } @@ -1405,7 +1575,7 @@ public final TreeMap groupBy( * @param eq The equality test. * @return Whether or not all elements in the list are equal according to the given equality test. */ - public boolean allEqual(final Equal eq) { + public final boolean allEqual(final Equal eq) { return isEmpty() || tail().isEmpty() || eq.eq(head(), tail().head()) && tail().allEqual(eq); } @@ -1441,7 +1611,7 @@ public final boolean isSuffixOf(final Equal eq, final List xs) { * @return A function that gets the length of a given list. */ public static F, Integer> length_() { - return a -> a.length(); + return List::length; } /** @@ -1451,7 +1621,17 @@ public static F, Integer> length_() { * @return The maximum element in this list according to the given ordering. */ public final A maximum(final Ord o) { - return foldLeft1(o.max); + return foldLeft1(o::max); + } + + /** + * Returns the maximum element in this list according to the given ordering. + * + * @param o An ordering for the elements of the list. + * @return The optional maximum element in this list according to the given ordering. + */ + public final Option maximumOption(final Ord o) { + return NonEmptyList.fromList(this).map(nel -> nel.maximum(o)); } /** @@ -1461,11 +1641,21 @@ public final A maximum(final Ord o) { * @return The minimum element in this list according to the given ordering. */ public final A minimum(final Ord o) { - return foldLeft1(o.min); + return foldLeft1(o::min); + } + + /** + * Returns the minimum element in this list according to the given ordering. + * + * @param o An ordering for the elements of the list. + * @return The optional minimum element in this list according to the given ordering. + */ + public final Option minimumOption(final Ord o) { + return NonEmptyList.fromList(this).map(nel -> nel.minimum(o)); } public final java.util.List toJavaList() { - return new java.util.LinkedList(toCollection()); + return new java.util.LinkedList<>(toCollection()); } /** @@ -1506,7 +1696,7 @@ public int size() { } private static final class Nil extends List { - public static final Nil INSTANCE = new Nil(); + public static final Nil INSTANCE = new Nil<>(); public A head() { throw error("head on empty list"); @@ -1557,24 +1747,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. */ @@ -1602,7 +1774,7 @@ public static F, List>> cons() { } public static F2, List> cons_() { - return (a, listA) -> cons(a, listA); + return List::cons; } /** @@ -1612,7 +1784,7 @@ public static F2, List> cons_() { * @return A function that prepends a value to the given list. */ public static F> cons(final List tail) { - return a -> tail.cons(a); + return tail::cons; } /** @@ -1633,7 +1805,7 @@ public static F, List> cons_(final A a) { * @return The list with the given element prepended. */ public static List cons(final A head, final List tail) { - return new Cons(head, tail); + return new Cons<>(head, tail); } /** @@ -1642,7 +1814,7 @@ public static List cons(final A head, final List tail) { * @return A function that determines whether a given list is empty. */ public static F, Boolean> isEmpty_() { - return as -> as.isEmpty(); + return List::isEmpty; } /** @@ -1651,7 +1823,7 @@ public static F, Boolean> isEmpty_() { * @return A function that determines whether a given list is not empty. */ public static F, Boolean> isNotEmpty_() { - return as -> as.isNotEmpty(); + return List::isNotEmpty; } /** @@ -1671,7 +1843,7 @@ public static List join(final List> o) { * @return A function that joins a list of lists using a bind operation. */ public static F>, List> join() { - return as -> join(as); + return List::join; } /** @@ -1702,7 +1874,7 @@ public static P2, List> unzip(final List> xs) { ba = ba.snoc(p._1()); bb = bb.snoc(p._2()); } - return P.p(ba.toList(), bb.toList()); + return p(ba.toList(), bb.toList()); } /** @@ -1713,7 +1885,7 @@ public static P2, List> unzip(final List> xs) { * @return A list of the given value replicated the given number of times. */ public static List replicate(final int n, final A a) { - List list = List.nil(); + List list = nil(); for (int i = 0; i < n; i++) { list = list.cons(a); } return list; } @@ -1728,7 +1900,7 @@ public static List replicate(final int n, final A a) { * to value (exclusive). */ public static List range(final int from, final int to) { - final Buffer buf = Buffer.empty(); + final Buffer buf = empty(); for (int i = from; i < to; i++) { buf.snoc(i); } @@ -1757,7 +1929,7 @@ public static List fromString(final String s) { * @return A first-class fromString. */ public static F> fromString() { - return s -> fromString(s); + return List::fromString; } /** @@ -1783,7 +1955,7 @@ public static String asString(final List cs) { * @return A first-class asString. */ public static F, String> asString() { - return cs -> asString(cs); + return List::asString; } /** @@ -1793,7 +1965,7 @@ public static F, String> asString() { * @return A list of one element containing the given value. */ public static List single(final A a) { - return cons(a, List.nil()); + return cons(a, List.nil()); } /** @@ -1808,7 +1980,7 @@ public static List single(final A a) { */ public static List iterateWhile(final F f, final F p, final A a) { return unfold( - o -> Option.iif(p2 -> p.f(o), P.p(o, f.f(o))) + o -> Option.iif(p2 -> p.f(o), p(o, f.f(o))) , a); } @@ -1821,7 +1993,7 @@ public static List iterateWhile(final F f, final F p, f * @return An associated value with the given key in the list of pairs. */ public static Option lookup(final Equal e, final List> x, final A a) { - return x.find(p -> e.eq(p._1(), a)).map(P2.__2()); + return x.find(p -> e.eq(p._1(), a)).map(P2.__2()); } /** @@ -1860,8 +2032,8 @@ public static F, F, List>> map_() { * and returns a list of the results. */ public static F> sequence_(final List> fs) { - return fs.foldRight(Function., List, B>lift(List.cons()), Function - .>constant(List.nil())); + return fs.foldRight(Function.lift(List.cons()), Function + .constant(List.nil())); } /** @@ -1931,7 +2103,7 @@ public Buffer snoc(final A a) { if (exported) copy(); - final Cons t = new Cons(a, List.nil()); + final Cons t = new Cons<>(a, List.nil()); if (tail == null) start = t; @@ -2004,7 +2176,7 @@ public Collection toCollection() { * @return An empty buffer. */ public static Buffer empty() { - return new Buffer(); + return new Buffer<>(); } /** @@ -2014,7 +2186,7 @@ public static Buffer empty() { * @return A buffer from the given list. */ public static Buffer fromList(final List as) { - final Buffer b = new Buffer(); + final Buffer b = new Buffer<>(); for (List xs = as; xs.isNotEmpty(); xs = xs.tail()) b.snoc(xs.head()); @@ -2037,7 +2209,7 @@ public static Buffer iterableBuffer(final Iterable i) { return b; } - @SuppressWarnings({"ObjectEquality"}) + @SuppressWarnings("ObjectEquality") private void copy() { List s = start; final Cons t = tail; @@ -2061,8 +2233,8 @@ private void copy() { * @param obj the other object to check for equality against. * @return true if this list is equal to the provided argument */ - @Override public boolean equals( final Object obj ) { - return Equal.equals0(List.class, this, obj, () -> Equal.listEqual(Equal.anyEqual())); + @Override public final boolean equals(final Object obj) { + return Equal.equals0(List.class, this, obj, () -> Equal.listEqual(Equal.anyEqual())); } /** @@ -2072,7 +2244,7 @@ private void copy() { * @return the hash code for this list. */ @Override - public int hashCode() { + public final int hashCode() { return Hash.listHash(Hash.anyHash()).hash(this); } @@ -2082,14 +2254,14 @@ public int hashCode() { * * @return a String representation of the list */ - @Override public String toString() { + @Override public final String toString() { return Show.listShow(Show.anyShow()).showS(this); } /** * True if and only if the list has one element. Runs in constant time. */ - public boolean isSingle() { + public final boolean isSingle() { return isNotEmpty() && tail().isEmpty(); } @@ -2154,8 +2326,8 @@ public F, P1>> modifyP1F(F> f) { } @Override - public F, Validation>> modifyValidationF(F> f) { - return l -> l.traverseValidation(f); + public F, Validation>> modifyValidationF(Semigroup s, F> f) { + return l -> l.traverseValidation(s, f); } @Override @@ -2181,15 +2353,15 @@ public static Traversal, A> traversal() { * Optional targeted on Cons head. */ public static Optional, A> head() { - return optional(l -> l.toOption(), a -> l -> l.>list(l, constant(cons_(a)))); + return optional(List::headOption, a -> l -> l.uncons((__, as) -> as.cons(a), l)); } /** * Optional targeted on Cons tail. */ public static Optional, List> tail() { - return optional(l -> l.>> list(none(), h -> tail -> some(tail)), - tail -> l -> l.list(l, h -> constant(List.cons(h, tail)))); + return optional(l -> l.uncons((__, tail) -> some(tail), none()), + tail -> l -> l.uncons((h, __) -> List.cons(h, tail), l)); } /** @@ -2203,7 +2375,7 @@ public static Prism, Unit> nil() { * Cons prism */ public static Prism, P2>> cons() { - return prism(l -> l.>>> list(none(), h -> tail -> some(P.p(h, tail))), c -> List.cons(c._1(), c._2())); + return prism(l -> l.>>> uncons((h, tail) -> some(p(h, tail)), none()), c -> List.cons(c._1(), c._2())); } } diff --git a/core/src/main/java/fj/data/Natural.java b/core/src/main/java/fj/data/Natural.java index 25cd11d5..e52f77ca 100644 --- a/core/src/main/java/fj/data/Natural.java +++ b/core/src/main/java/fj/data/Natural.java @@ -1,11 +1,16 @@ package fj.data; import static fj.Bottom.error; + +import fj.Equal; import fj.F; -import fj.F2; + import static fj.Monoid.naturalAdditionMonoid; import static fj.Monoid.naturalMultiplicationMonoid; import static fj.Function.curry; + +import fj.Hash; +import fj.Show; import fj.data.vector.V2; import fj.data.vector.V; @@ -32,7 +37,7 @@ private Natural(final BigInteger i) { */ public static Option natural(final BigInteger i) { return i.compareTo(BigInteger.ZERO) < 0 - ? Option.none() + ? Option.none() : Option.some(new Natural(i)); } @@ -40,7 +45,7 @@ public static Option natural(final BigInteger i) { * A function that returns the natural number equal to a given BigInteger */ public static final F> fromBigInt = - i -> natural(i); + Natural::natural; /** * Returns the natural number equal to the given long @@ -77,7 +82,7 @@ public Natural succ() { * @return A function that returns the successor of a given natural number. */ public static F succ_() { - return natural -> natural.succ(); + return Natural::succ; } /** @@ -95,7 +100,7 @@ public Option pred() { * @return A function that returns the predecessor of a given natural number, or None if it's zero. */ public static F> pred_() { - return natural -> natural.pred(); + return Natural::pred; } /** @@ -111,7 +116,7 @@ public Natural add(final Natural n) { /** * A function that adds two natural numbers. */ - public static final F> add = curry((n1, n2) -> n1.add(n2)); + public static final F> add = curry(Natural::add); /** @@ -121,7 +126,7 @@ public Natural add(final Natural n) { * @return The difference between the two numbers, if this number is larger than the given one. Otherwise none. */ public Option subtract(final Natural n) { - return natural(n.value.subtract(value)); + return natural(value.subtract(n.value)); } /** @@ -143,7 +148,7 @@ public Natural multiply(final Natural n) { /** * A function that multiplies a natural number by another. */ - public static final F> multiply = curry((n1, n2) -> n1.multiply(n2)); + public static final F> multiply = curry(Natural::multiply); /** @@ -245,7 +250,7 @@ public int intValue() { /** * A function that returns the BigInteger value of a given Natural. */ - public static final F bigIntegerValue = n -> n.bigIntegerValue(); + public static final F bigIntegerValue = Natural::bigIntegerValue; /** * Sums a stream of natural numbers. @@ -286,4 +291,20 @@ public static Natural sum(final List ns) { public static Natural product(final List ns) { return naturalMultiplicationMonoid.sumLeft(ns); } + + + @Override + public int hashCode() { + return Hash.naturalHash.hash(this); + } + + @Override + public boolean equals(final Object that) { + return Equal.equals0(Natural.class, this, that, Equal.naturalEqual); + } + + @Override + public String toString() { + return Show.naturalShow.showS(this); + } } diff --git a/core/src/main/java/fj/data/NonEmptyList.java b/core/src/main/java/fj/data/NonEmptyList.java index 0ebffc11..96bf444a 100644 --- a/core/src/main/java/fj/data/NonEmptyList.java +++ b/core/src/main/java/fj/data/NonEmptyList.java @@ -6,14 +6,14 @@ import java.util.Collection; import java.util.Iterator; +import static fj.Function.flip; import static fj.Function.identity; +import static fj.Function.uncurryF2; import static fj.data.Option.some; import static fj.data.Option.somes; /** * Provides an in-memory, immutable, singly linked list with total head and tail. - * - * @version %build.number% */ public final class NonEmptyList implements Iterable { /** @@ -72,6 +72,16 @@ public NonEmptyList snoc(final A a) { */ public int length() { return 1 + tail.length(); } + /** + * Appends the given list to this list. + * + * @param as The list to append. + * @return A new list with the given list appended. + */ + public NonEmptyList append(final List as) { + return nel(head, tail.append(as)); + } + /** * Appends the given list to this list. * @@ -79,7 +89,7 @@ public NonEmptyList snoc(final A a) { * @return A new list with the given list appended. */ public NonEmptyList append(final NonEmptyList as) { - final List.Buffer b = new List.Buffer(); + final List.Buffer b = new List.Buffer<>(); b.append(tail); b.snoc(as.head); b.append(as.tail); @@ -87,6 +97,40 @@ public NonEmptyList append(final NonEmptyList as) { return nel(head, bb); } + /** + * Performs a right-fold reduction across this list. This function uses O(length) stack space. + */ + public final A foldRight1(final F> f) { + return reverse().foldLeft1(flip(f)); + } + + /** + * Performs a right-fold reduction across this list. This function uses O(length) stack space. + */ + public final A foldRight1(final F2 f) { + return reverse().foldLeft1(flip(f)); + } + + /** + * Performs a left-fold reduction across this list. This function runs in constant space. + */ + public final A foldLeft1(final F> f) { + return foldLeft1(uncurryF2(f)); + } + + /** + * Performs a left-fold reduction across this list. This function runs in constant space. + */ + public final A foldLeft1(final F2 f) { + A x = head; + + for (List xs = tail; !xs.isEmpty(); xs = xs.tail()) { + x = f.f(x, xs.head()); + } + + return x; + } + /** * Maps the given function across this list. * @@ -104,16 +148,14 @@ public NonEmptyList map(final F f) { * @return A new list after performing the map, then final join. */ public NonEmptyList bind(final F> f) { - final List.Buffer b = new List.Buffer(); + final List.Buffer b = new List.Buffer<>(); final NonEmptyList p = f.f(head); b.snoc(p.head); b.append(p.tail); - tail.foreachDoEffect(new Effect1() { - public void f(final A a) { - final NonEmptyList p = f.f(a); - b.snoc(p.head); - b.append(p.tail); - } + tail.foreachDoEffect(a -> { + final NonEmptyList p1 = f.f(a); + b.snoc(p1.head); + b.append(p1.tail); }); final List bb = b.toList(); return nel(bb.head(), bb.tail()); @@ -125,9 +167,10 @@ public void f(final A a) { * @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(list -> fromList(list), Conversions.Stream_List())).toList())).some(); + .map(f).toList())).some(); } /** @@ -137,7 +180,7 @@ public NonEmptyList> sublists() { * @return A NonEmptyList of the tails of this list. */ public NonEmptyList> tails() { - return fromList(somes(toList().tails().map(list -> fromList(list)))).some(); + return fromList(somes(toList().tails().map(NonEmptyList::fromList))).some(); } /** @@ -182,6 +225,28 @@ public NonEmptyList sort(final Ord o) { return nel(list.head(), list.tail()); } + + /** + * Returns the minimum element in this non empty list according to the given ordering. + * + * @param o An ordering for the elements of this non empty list. + * @return The minimum element in this list according to the given ordering. + */ + public final A minimum(final Ord o) { + return foldLeft1(o::min); + } + + /** + * Returns the maximum element in this non empty list according to the given ordering. + * + * @param o An ordering for the elements of this non empty list. + * @return The maximum element in this list according to the given ordering. + */ + public final A maximum(final Ord o) { + return foldLeft1(o::max); + } + + /** * Zips this non empty list with the given non empty list to produce a list of pairs. If this list and the given list * have different lengths, then the longer list is normalised so this function never fails. @@ -268,7 +333,7 @@ public Collection toCollection() { * @return A function that takes a non-empty list to a list. */ public static F, List> toList_() { - return as -> as.toList(); + return NonEmptyList::toList; } /** @@ -279,7 +344,7 @@ public static F, List> toList_() { * @return A non-empty list with the given head and tail. */ public static NonEmptyList nel(final A head, final List tail) { - return new NonEmptyList(head, tail); + return new NonEmptyList<>(head, tail); } /** @@ -310,7 +375,7 @@ public static F> nel() { */ public static Option> fromList(final List as) { return as.isEmpty() ? - Option.>none() : + Option.none() : some(nel(as.head(), as.tail())); } @@ -330,7 +395,7 @@ public static Option> fromList(final List as) { * @return true if this list is equal to the provided argument */ @Override public boolean equals( final Object obj ) { - return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual())); + return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual())); } @Override public int hashCode() { diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index c9f9f9f2..9831c48c 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -1,45 +1,28 @@ 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 java.lang.Class; +import java.util.*; + +import static fj.Bottom.error; import static fj.Function.*; import static fj.P.p; -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.Show.optionShow; - -import java.util.Collection; -import java.util.Iterator; +import static fj.Unit.unit; +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.data.optic.Prism.prism; /** * 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() { @@ -47,7 +30,7 @@ private Option() { } @Override - public String toString() { + public final String toString() { return optionShow(Show.anyShow()).showS(this); } @@ -91,7 +74,7 @@ public final boolean isNone() { * @return A function that returns true if a given optional value has a value, otherwise false. */ public static F, Boolean> isSome_() { - return a -> a.isSome(); + return Option::isSome; } /** @@ -100,7 +83,7 @@ public static F, Boolean> isSome_() { * @return A function that returns false if a given optional value has a value, otherwise true. */ public static F, Boolean> isNone_() { - return a -> a.isNone(); + return Option::isNone; } /** @@ -187,7 +170,7 @@ public final A valueE(final String message) { * @return A new optional value after the given function has been applied to its element. */ public final Option map(final F f) { - return isSome() ? some(f.f(some())) : Option.none(); + return isSome() ? some(f.f(some())) : Option.none(); } /** @@ -227,7 +210,7 @@ public final void foreachDoEffect(final Effect1 f) { * @return A new optional value whose value matches the given predicate if it has one. */ public final Option filter(final F f) { - return isSome() ? f.f(some()) ? this : Option.none() : Option.none(); + return isSome() ? f.f(some()) ? this : Option.none() : Option.none(); } /** @@ -237,7 +220,7 @@ public final Option filter(final F f) { * @return A new optional value after performing the map, then final join. */ public final Option bind(final F> f) { - return isSome() ? f.f(some()) : Option.none(); + return isSome() ? f.f(some()) : Option.none(); } /** @@ -362,38 +345,38 @@ public final Option bind(final Option ob, final Option oc, } public final Option> bindProduct(final Option ob) { - return bind(ob, P.p2()); + return bind(ob, P.p2()); } public final Option> bindProduct(final Option ob, final Option oc) { - return bind(ob, oc, P.p3()); + 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()); + 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()); + return bind(ob, oc, od, oe, P.p5()); } public final Option> bindProduct(final Option ob, final Option oc, final Option od, final Option oe, final Option of) { - return bind(ob, oc, od, oe, of, P.p6()); + return bind(ob, oc, od, oe, of, P.p6()); } public final Option> bindProduct(final Option ob, final Option oc, final Option od, final Option oe, final Option of, final Option og) { - return bind(ob, oc, od, oe, of, og, P.p7()); + return bind(ob, oc, od, oe, of, og, P.p7()); } public final Option> bindProduct(final Option ob, final Option oc, final Option od, final Option oe, final Option of, final Option og, final Option oh) { - return bind(ob, oc, od, oe, of, og, oh, P.p8()); + return bind(ob, oc, od, oe, of, og, oh, P.p8()); } /** @@ -407,45 +390,331 @@ public final Option sequence(final Option o) { return bind(c); } - public Either> traverseEither(F> f) { - return map(a -> f.f(a).right().map(b -> some(b))).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> sequenceEither(final Option> option) { + return option.traverseEitherRight(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, R> sequenceEitherLeft(final Option> option) { + return option.traverseEitherLeft(identity()); + } + + /** + * 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> 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()); + } + + /** + * 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 IO> traverseIO(F> f) { - return map(a -> IOFunctions.map(f.f(a), b -> some(b))).orSome(IOFunctions.lazy(() -> none())); + /** + * 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 List> traverseList(F> f) { - return map(a -> f.f(a).map(b -> some(b))).orSome(List.list()); + /** + * 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 Option> traverseOption(F> f) { - return map(f); + /** + * 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 Stream> traverseStream(F> f) { - return map(a -> f.f(a).map(b -> some(b))).orSome(Stream.nil()); + /** + * 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 P1> traverseP1(F> f) { - return map(a -> f.f(a).map(b -> some(b))).orSome(P.p(none())); + /** + * 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 Seq> traverseSeq(F> f) { - return map(a -> f.f(a).map(b -> some(b))).orSome(Seq.empty()); + /** + * 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 Set> traverseSet(Ord ord, F> f) { - Ord> optOrd = Ord.optionOrd(ord); - return map(a -> f.f(a).map(optOrd, b -> some(b))).orSome(Set.empty(optOrd)); + /** + * 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); } - public F2, F>, Set>> traverseSet() { - return (o, f) -> traverseSet(o, f); + /** + * 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 Validation> traverseValidation(F> f) { - return map(a -> f.f(a).map(b -> some(b))).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; } /** @@ -456,7 +725,7 @@ public Validation> traverseValidation(F> * optional value. */ public final Option apply(final Option> of) { - return of.bind(f -> map(a -> f.f(a))); + return of.bind(f -> map(f)); } /** @@ -487,7 +756,7 @@ public final Option orElse(final Option o) { * @return An either projection of this optional value. */ public final Either toEither(final F0 x) { - return isSome() ? Either.right(some()) : Either.left(x.f()); + return isSome() ? Either.right(some()) : Either.left(x.f()); } /** @@ -498,7 +767,7 @@ public final Either toEither(final F0 x) { * @return An either projection of this optional value. */ public final Either toEither(final X x) { - return isSome() ? Either.right(some()) : Either.left(x); + return isSome() ? Either.right(some()) : Either.left(x); } public final Validation toValidation(final X x) { @@ -512,7 +781,7 @@ public final Validation toValidation(final X x) { * return in left. */ public static F, F>> toEither() { - return curry((a, x) -> a.toEither(x)); + return curry(Option::toEither); } /** @@ -521,7 +790,7 @@ public static F, F>> toEither() { * @return A list projection of this optional value. */ public final List toList() { - return isSome() ? cons(some(), List.nil()) : List.nil(); + return isSome() ? cons(some(), List.nil()) : List.nil(); } /** @@ -530,7 +799,7 @@ public final List toList() { * @return A stream projection of this optional value. */ public final Stream toStream() { - return isSome() ? Stream.nil().cons(some()) : Stream.nil(); + return isSome() ? Stream.nil().cons(some()) : Stream.nil(); } /** @@ -538,9 +807,9 @@ public final Stream toStream() { * * @return An array projection of this optional value. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final Array toArray() { - return isSome() ? Array.array(some()) : Array.empty(); + return isSome() ? Array.array(some()) : Array.empty(); } /** @@ -549,7 +818,7 @@ public final Array toArray() { * @param c The class type of the array to return. * @return An array projection of this optional value. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final Array toArray(final Class c) { if (isSome()) { final A[] a = (A[]) java.lang.reflect.Array.newInstance(c.getComponentType(), 1); @@ -604,8 +873,8 @@ public final boolean exists(final F f) { } @Override - public boolean equals(Object other) { - return Equal.equals0(Option.class, this, other, () -> Equal.optionEqual(Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(Option.class, this, other, () -> Equal.optionEqual(Equal.anyEqual())); } /** @@ -637,7 +906,7 @@ public A some() { public static F> some_() { - return t -> some(t); + return Option::some; } /** @@ -647,7 +916,7 @@ public static F> some_() { * @return An optional value that has a value of the given argument. */ public static Option some(final T t) { - return new Some(t); + return new Some<>(t); } public static F> none_() { @@ -660,7 +929,7 @@ public static F> none_() { * @return An optional value that has no value. */ public static Option none() { - return new None(); + return new None<>(); } /** @@ -671,7 +940,7 @@ public static Option none() { * @return If t == null then return none, otherwise, return it in some. */ public static Option fromNull(final T t) { - return t == null ? Option.none() : some(t); + return t == null ? Option.none() : some(t); } /** @@ -681,7 +950,19 @@ public static Option fromNull(final T t) { * @return If t == null then return none, otherwise, return it in some. */ public static F> fromNull() { - return t -> fromNull(t); + return Option::fromNull; + } + + /** + * First-class catamorphism for Option: return a function that will performs + * a reduction on an optional value using the given arguments. + * + * @param none The value to return if this optional value has no value. + * @param some The function to apply to the value of this optional value. + * @return the reducing function. + */ + public static final F, B> option_(final B none, final F some) { + return o -> o.option(none, some); } /** @@ -696,17 +977,27 @@ 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. */ public static Option> sequence(final List> a) { return a.isEmpty() ? - some(List.nil()) : + some(List.nil()) : 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. @@ -718,7 +1009,7 @@ public static Option> sequence(final List> a) { * on that argument, otherwise, returns no value. */ public static Option iif(final F f, final A a) { - return f.f(a) ? some(a) : Option.none(); + return f.f(a) ? some(a) : Option.none(); } /** @@ -731,7 +1022,7 @@ public static Option iif(final F f, final A a) { * no value. */ public static Option iif(final boolean p, final F0 a) { - return p ? some(a.f()) : Option.none(); + return p ? some(a.f()) : Option.none(); } /** @@ -754,7 +1045,7 @@ public static Option iif(final boolean p, final A a) { * holds on that argument, or no value otherwise. */ public static F2, A, Option> iif() { - return (p, a) -> iif(p, a); + return Option::iif; } /** @@ -764,7 +1055,7 @@ public static F2, A, Option> iif() { * @return All the values in the given list. */ public static List somes(final List> as) { - return as.filter(Option.isSome_()).map(o -> o.some()); + return as.filter(Option.isSome_()).map(o -> o.some()); } @@ -775,7 +1066,7 @@ public static List somes(final List> as) { * @return All the values in the given stream. */ public static Stream somes(final Stream> as) { - return as.filter(Option.isSome_()).map(o -> o.some()); + return as.filter(Option.isSome_()).map(o -> o.some()); } /** @@ -792,7 +1083,7 @@ public static Option fromString(final String s) { } @Override - public int hashCode() { + public final int hashCode() { return Hash.optionHash(Hash.anyHash()).hash(this); } @@ -804,7 +1095,7 @@ public int hashCode() { * or no value if the string is empty. */ public static F> fromString() { - return s -> fromString(s); + return Option::fromString; } /** @@ -832,7 +1123,7 @@ public static F, F, Option>> liftM2(final F Option liftM2(final Option ob, final F2 f) { + public final Option liftM2(final Option ob, final F2 f) { return bind(a -> ob.map(b -> f.f(a, b))); } @@ -851,7 +1142,7 @@ public static F>, F, Option>> bind() { * @return A function that joins an Option of an Option to make a single Option. */ public static F>, Option> join() { - return option -> join(option); + return Option::join; } /** @@ -883,4 +1174,34 @@ public static F>, Option> join() { * A function that parses a string to a short. */ public static final F> parseShort = s -> parseShort(s).toOption(); + + public static final class Optic { + + private Optic() { + throw new UnsupportedOperationException(); + } + + /** + * None prism + */ + public static Prism, Unit> none() { + return prism(o -> o.option(Option.some(Unit.unit()), a -> Option.none()), u -> Option.none()); + } + + /** + * Polymorphic Some prism + */ + public static PPrism, Option, A, B> pSome() { + return pPrism(o -> o., A>>map(Either::right).orSome(Either.left(Option.none())), Option::some); + } + + /** + * Monomorphic Some prism + */ + public static Prism, A> some() { + return new Prism<>(pSome()); + } + + } + } diff --git a/core/src/main/java/fj/data/PriorityQueue.java b/core/src/main/java/fj/data/PriorityQueue.java new file mode 100644 index 00000000..f685c36d --- /dev/null +++ b/core/src/main/java/fj/data/PriorityQueue.java @@ -0,0 +1,222 @@ +package fj.data; + +import fj.Equal; +import fj.F; +import fj.F2; +import fj.Monoid; +import fj.Ord; +import fj.P; +import fj.P2; +import fj.Show; +import fj.data.fingertrees.FingerTree; + +import static fj.Function.compose; +import static fj.data.Option.none; +import static fj.data.Option.some; + +/** + * A priority queue implementation backed by a + * {@link fj.data.fingertrees.FingerTree}. The finger tree nodes are + * 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). + */ +public final class PriorityQueue { + + private final FingerTree> ftree; + private final Equal equal; + + private PriorityQueue(Equal e, FingerTree> ft) { + equal = e; + ftree = ft; + } + + /** + * Creates a priority queue from a finger tree. + */ + public static PriorityQueue priorityQueue(Equal e, FingerTree> ft) { + return new PriorityQueue<>(e, ft); + } + + /** + * Creates an empty priority queue. + * + * @param m A monoid to combine node annotations. + * @param e A value to compare key equality. + */ + public static PriorityQueue empty(Monoid m, Equal e) { + return priorityQueue(e, FingerTree.empty(m, P2.__1())); + } + + /** + * An empty priority queue with integer priorities. + */ + public static PriorityQueue emptyInt() { + return empty(Monoid.intMaxMonoid, Equal.intEqual); + } + + /** + * Maps the values in each node with function f. + */ + public PriorityQueue map(F f) { + return priorityQueue(equal, + ftree.map(P2.map2_(f), + FingerTree.measured(ftree.measured().monoid(), P2.__1()) + ) + ); + } + + /** + * Filters nodes based on the value inside each node. + */ + public PriorityQueue filterValues(F f) { + return priorityQueue(equal, ftree.filter(p2 -> f.f(p2._2()))); + } + + /** + * Filters the nodes based on the annotation of each node. + */ + public PriorityQueue filterKeys(F f) { + return priorityQueue(equal, ftree.filter(p2 -> f.f(p2._1()))); + } + + /** + * Is the tree empty? + */ + public boolean isEmpty() { + return ftree.isEmpty(); + } + + /** + * If the tree is not empty, returns the node with highest priority otherwise returns nothing. + */ + public Option> top() { + return unqueue(none(), (top, tail) -> some(top)); + } + + /** + * Returns all the elements of the queue with the highest (same) priority. + */ + public List> topN() { + return toStream().uncons( + List.nil(), + top -> tail -> List.cons(top, tail._1().takeWhile(compose(equal.eq(top._1()), P2.__1())).toList()) + ); + } + + /** + * Adds a node with priority k and value a. This operation take O(1). + */ + public PriorityQueue enqueue(K k, A a) { + return priorityQueue(equal, ftree.snoc(P.p(k, a))); + } + + /** + * Adds nodes using the list of products with priority k and value a. This operation takes O(list.length()). + */ + public PriorityQueue enqueue(List> list) { + return list.foldLeft((pq, p) -> pq.enqueue(p._1(), p._2()), this); + } + + /** + * Does the priority k exist already? + */ + public boolean contains(final K k) { + return !ftree.split(equal.eq(k))._2().isEmpty(); + } + + /** + * Adds nodes using the iterable of products with priority k and value a. + */ + public PriorityQueue enqueue(Iterable> it) { + PriorityQueue result = this; + for (P2 p: it) { + result = result.enqueue(p); + } + return result; + } + + /** + * Adds a node with priority k and value a. This operation take O(1). + */ + public PriorityQueue enqueue(P2 p) { + return enqueue(p._1(), p._2()); + } + + /** + * Removes the node with the highest priority. + */ + public PriorityQueue dequeue() { + return unqueue(this, (top, tail) -> tail); + } + + /** + * Returns a tuple of the node with the highest priority and the rest of the priority queue. + */ + public P2>, PriorityQueue> topDequeue() { + return unqueue(P.p(none(), this), (top, tail) -> P.p(some(top), tail)); + } + + /** + * Performs a reduction on this priority queue using the given arguments. + * + * @param empty The value to return if this queue is empty. + * @param topDequeue The function to apply to the top priority element and the tail of the queue (without its top element). + * @return A reduction on this queue. + */ + public B unqueue(B empty, F2, PriorityQueue, B> topDequeue) { + K top = ftree.measure(); + P2>, FingerTree>> p = ftree.split(equal.eq(top)); + return p._2().uncons( + empty, + (head, tail) -> topDequeue.f(head, priorityQueue(equal, p._1().append(tail))) + ); + } + + /** + * Removes the top n elements with the highest priority. + */ + public PriorityQueue dequeue(int n) { + int i = n; + PriorityQueue result = this; + while (i > 0) { + i--; + result = result.dequeue(); + } + return result; + } + + /** + * Does the top of the queue have lower priority than k? + */ + public boolean isLessThan(Ord ok, K k) { + return top().option(true, p -> ok.isLessThan(p._1(), k)); + } + + public boolean isGreaterThan(Ord ok, K k) { + return top().option(false, p -> ok.isGreaterThan(p._1(), k)); + } + + public boolean isEqual(Ord ok, K k) { + return top().option(false, p -> ok.eq(p._1(), k)); + } + + /** + * Returns a stream of products with priority k and value a. + */ + public Stream> toStream() { + return unqueue(Stream.nil(), (top, tail) -> Stream.cons(top, () -> tail.toStream())); + } + + /** + * Returns a list of products with priority k and value a. + */ + public List> toList() { + return toStream().toList(); + } + + public String toString() { + return Show.priorityQueueShow(Show.anyShow(), Show.anyShow()).showS(this); + } + +} diff --git a/core/src/main/java/fj/data/Reader.java b/core/src/main/java/fj/data/Reader.java index 34a98c72..49010e31 100644 --- a/core/src/main/java/fj/data/Reader.java +++ b/core/src/main/java/fj/data/Reader.java @@ -1,49 +1,47 @@ package fj.data; import fj.F; -import fj.F1Functions; /** * 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 { - private F function; + private final F function; public Reader(F f) { function = f; } - public F getFunction() { + public final F getFunction() { return function; } public static Reader unit(F f) { - return new Reader(f); + return new Reader<>(f); } public static Reader constant(B b) { return unit(a -> b); } - public B f(A a) { + public final B f(A a) { return function.f(a); } - public Reader map(F f) { - return unit(F1Functions.andThen(function, f)); + public final Reader map(F f) { + return unit(function.andThen(f)); } - public Reader andThen(F f) { + public final Reader andThen(F f) { return map(f); } - public Reader flatMap(F> f) { + public final Reader flatMap(F> f) { return unit(a -> f.f(function.f(a)).f(a)); } - public Reader bind(F> f) { + public final Reader bind(F> f) { return flatMap(f); } diff --git a/core/src/main/java/fj/data/SafeIO.java b/core/src/main/java/fj/data/SafeIO.java index 2935ca83..bcc16bf6 100644 --- a/core/src/main/java/fj/data/SafeIO.java +++ b/core/src/main/java/fj/data/SafeIO.java @@ -1,14 +1,15 @@ package fj.data; -import java.io.IOException; - -/** - * Created by MarkPerry on 3/07/2014. - */ +@FunctionalInterface public interface SafeIO extends IO { @Override - public A run(); + A run(); + + @Override + default A f() { + return run(); + } } diff --git a/core/src/main/java/fj/data/Seq.java b/core/src/main/java/fj/data/Seq.java index a29d97c0..59a46df7 100644 --- a/core/src/main/java/fj/data/Seq.java +++ b/core/src/main/java/fj/data/Seq.java @@ -1,20 +1,21 @@ 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; -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. @@ -22,7 +23,7 @@ public final class Seq implements Iterable { private static final Measured ELEM_MEASURED = measured(intAdditionMonoid, Function.constant(1)); private static final MakeTree MK_TREE = FingerTree.mkTree(ELEM_MEASURED); - private static final Seq EMPTY = new Seq(MK_TREE.empty()); + private static final Seq EMPTY = new Seq<>(MK_TREE.empty()); @SuppressWarnings("unchecked") private static MakeTree mkTree() { @@ -52,7 +53,7 @@ public static Seq empty() { @Override public boolean equals(Object other) { - return Equal.equals0(Seq.class, this, other, () -> Equal.seqEqual(Equal.anyEqual())); + return Equal.equals0(Seq.class, this, other, () -> Equal.seqEqual(Equal.anyEqual())); } /** @@ -62,7 +63,7 @@ public boolean equals(Object other) { * @return A new sequence with the given element in it. */ public static Seq single(final A a) { - return new Seq(Seq.mkTree().single(a)); + return new Seq<>(Seq.mkTree().single(a)); } /** @@ -77,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); } @@ -106,7 +91,7 @@ public static Seq listSeq(final List list) { * @return A sequence with the elements of the iterable. */ public static Seq iterableSeq(final Iterable i) { - Seq s = Seq.empty(); + Seq s = empty(); for (final A a: i) { s = s.snoc(a); } @@ -146,7 +131,7 @@ public static Seq fromJavaList(final java.util.List list) { * @return A new sequence with the given element at the front. */ public Seq cons(final A a) { - return new Seq(ftree.cons(a)); + return new Seq<>(ftree.cons(a)); } /** @@ -156,7 +141,7 @@ public Seq cons(final A a) { * @return A new sequence with the given element at the end. */ public Seq snoc(final A a) { - return new Seq(ftree.snoc(a)); + return new Seq<>(ftree.snoc(a)); } /** @@ -214,7 +199,7 @@ public List toList() { /** * Converts the sequence to a java.util.List */ - public final java.util.List toJavaList() { + public java.util.List toJavaList() { return new AbstractList() { @Override public A get(int i) { return index(i); } @Override public Iterator iterator() { return Seq.this.iterator(); } @@ -227,7 +212,7 @@ public final java.util.List toJavaList() { * * @return A iterator for this seq. */ - public final Iterator iterator() { + public Iterator iterator() { return new Iterator() { private FingerTree ftree = Seq.this.ftree; @@ -263,7 +248,7 @@ public String toString() { * @return A new sequence with the given sequence appended to this one. */ public Seq append(final Seq as) { - return new Seq(ftree.append(as.ftree)); + return new Seq<>(ftree.append(as.ftree)); } /** @@ -275,6 +260,18 @@ public boolean isEmpty() { return ftree.isEmpty(); } + /** + * Inserts the element at the given index. This is an O(log(n)) operation. + * + * @param index The index of the element to return. + * @return The sequence with the element inserted at the given index, + * or throws an error if the index is out of bounds. + */ + public Seq insert(int index, A a) { + final P2, Seq> p = split(index); + return p._1().append(single(a)).append(p._2()); + } + /** * Checks if this sequence is not empty. * @@ -312,7 +309,7 @@ public P2, Seq> split(final int i) { */ public A index(final int i) { checkBounds(i); - return ftree.lookup(Function.identity(), i)._2(); + return ftree.lookup(Function.identity(), i)._2(); } /** @@ -370,13 +367,385 @@ public B foldRight(final F2 f, final B z) { return ftree.foldRight(f, z); } + + public Seq filter(F f) { + return foldLeft((acc, a) -> f.f(a) ? acc.snoc(a) : acc, empty()); + } + @Override public int hashCode() { return Hash.seqHash(Hash.anyHash()).hash(this); } public Seq map(F f) { - return new Seq(ftree.map(f, Seq.elemMeasured())); + 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()); + } + + /** + * 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/main/java/fj/data/Set.java b/core/src/main/java/fj/data/Set.java index 6c23d6d5..4c0aeaa0 100644 --- a/core/src/main/java/fj/data/Set.java +++ b/core/src/main/java/fj/data/Set.java @@ -31,7 +31,7 @@ public final boolean isEmpty() { return this instanceof Empty; } - @SuppressWarnings({"ClassEscapesDefinedScope"}) + @SuppressWarnings("ClassEscapesDefinedScope") abstract Color color(); abstract Set l(); @@ -115,20 +115,20 @@ public Set r() { public final P2> update(final A a, final F f) { return isEmpty() ? P.p(false, this) - : tryUpdate(a, f).either(a2 -> P.p(true, delete(a).insert(a2)), Function.>>identity()); + : tryUpdate(a, f).either(a2 -> P.p(true, delete(a).insert(a2)), Function.identity()); } private Either>> tryUpdate(final A a, final F f) { if (isEmpty()) return right(P.p(false, this)); else if (ord.isLessThan(a, head())) - return l().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree(ord, color(), set._2(), head(), r())) : set); + return l().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree<>(ord, color(), set._2(), head(), r())) : set); else if (ord.eq(a, head())) { final A h = f.f(head()); return ord.eq(head(), h) ? Either - .>>right(P.p(true, (Set) new Tree(ord, color(), l(), h, r()))) - : Either.>>left(h); - } else return r().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree(ord, color(), l(), head(), set._2())) : set); + .right(P.p(true, (Set) new Tree<>(ord, color(), l(), h, r()))) + : Either.left(h); + } else return r().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree<>(ord, color(), l(), head(), set._2())) : set); } /** @@ -138,21 +138,21 @@ else if (ord.eq(a, head())) { * @return the empty set. */ public static Set empty(final Ord ord) { - return new Empty(ord); + return new Empty<>(ord); } @Override - public boolean equals(Object other) { - return Equal.equals0(Set.class, this, other, () -> Equal.setEqual(Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(Set.class, this, other, () -> Equal.setEqual(Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.setHash(Hash.anyHash()).hash(this); } @Override - public String toString() { + public final String toString() { return Show.setShow(Show.anyShow()).showS(this); } @@ -173,7 +173,7 @@ public final boolean member(final A x) { * @return A function that returns true if the given element if a member of the given set. */ public static F, F> member() { - return curry((s, a) -> s.member(a)); + return curry(Set::member); } /** @@ -197,28 +197,28 @@ public static F, Set>> insert() { private Set ins(final A x) { return isEmpty() - ? new Tree(ord, Color.R, empty(ord), x, empty(ord)) + ? new Tree<>(ord, Color.R, empty(ord), x, empty(ord)) : ord.isLessThan(x, head()) ? balance(ord, color(), l().ins(x), head(), r()) : ord.eq(x, head()) - ? new Tree(ord, color(), l(), x, r()) + ? new Tree<>(ord, color(), l(), x, r()) : balance(ord, color(), l(), head(), r().ins(x)); } private Set makeBlack() { - return new Tree(ord, Color.B, l(), head(), r()); + return new Tree<>(ord, Color.B, l(), head(), r()); } - @SuppressWarnings({"SuspiciousNameCombination"}) + @SuppressWarnings("SuspiciousNameCombination") private static Tree tr(final Ord o, final Set a, final A x, final Set b, final A y, final Set c, final A z, final Set d) { - return new Tree(o, Color.R, new Tree(o, Color.B, a, x, b), y, new Tree(o, Color.B, c, z, d)); + return new Tree<>(o, Color.R, new Tree<>(o, Color.B, a, x, b), y, new Tree<>(o, Color.B, c, z, d)); } private static Set balance(final Ord ord, final Color c, final Set l, final A h, final Set r) { - return c == Color.B && l.isTR() && l.l().isTR() ? tr(ord, l.l().l(), l.l().head(), l.l().r(), l.head(), l.r(), h, r) : c == Color.B && l.isTR() && l.r().isTR() ? tr(ord, l.l(), l.head(), l.r().l(), l.r().head(), l.r().r(), h, r) : c == Color.B && r.isTR() && r.l().isTR() ? tr(ord, l, h, r.l().l(), r.l().head(), r.l().r(), r.head(), r.r()) : c == Color.B && r.isTR() && r.r().isTR() ? tr(ord, l, h, r.l(), r.head(), r.r().l(), r.r().head(), r.r().r()) : new Tree(ord, c, l, h, r); + return c == Color.B && l.isTR() && l.l().isTR() ? tr(ord, l.l().l(), l.l().head(), l.l().r(), l.head(), l.r(), h, r) : c == Color.B && l.isTR() && l.r().isTR() ? tr(ord, l.l(), l.head(), l.r().l(), l.r().head(), l.r().r(), h, r) : c == Color.B && r.isTR() && r.l().isTR() ? tr(ord, l, h, r.l().l(), r.l().head(), r.l().r(), r.head(), r.r()) : c == Color.B && r.isTR() && r.r().isTR() ? tr(ord, l, h, r.l(), r.head(), r.r().l(), r.r().head(), r.r().r()) : new Tree<>(ord, c, l, h, r); } private boolean isTR() { @@ -288,7 +288,7 @@ public final B foldMapRight(final F f, final Monoid m) { * @return a list representation of this set. */ public final List toList() { - return foldMap(List.cons(List.nil()), Monoid.listMonoid()); + return foldMap(List.cons(List.nil()), Monoid.listMonoid()); } /** @@ -306,7 +306,7 @@ public final java.util.Set toJavaSet() { * @return a java.util.HashSet representation of this set. */ public final java.util.HashSet toJavaHashSet() { - return new java.util.HashSet(toStream().toCollection()); + return new java.util.HashSet<>(toStream().toCollection()); } /** @@ -315,7 +315,7 @@ public final java.util.HashSet toJavaHashSet() { * @return a java.util.TreeSet representation of this set. */ public final java.util.TreeSet toJavaTreeSet() { - return new java.util.TreeSet(toStream().toCollection()); + return new java.util.TreeSet<>(toStream().toCollection()); } /** @@ -323,8 +323,8 @@ public final java.util.TreeSet toJavaTreeSet() { * * @return a java.util.List representation of this set. */ - public final java.util.List toJavaList() { - return new java.util.ArrayList(toStream().toCollection()); + public final java.util.List toJavaList() { + return new java.util.ArrayList<>(toStream().toCollection()); } /** @@ -333,7 +333,7 @@ public final java.util.List toJavaList() { * @return a list representation of this set in reverse order. */ public final List toListReverse() { - return foldMapRight(List.cons(List.nil()), Monoid.listMonoid()); + return foldMapRight(List.cons(List.nil()), Monoid.listMonoid()); } /** @@ -394,7 +394,7 @@ public final Set union(final Set s) { * @see #union(Set) */ public static F, F, Set>> union() { - return curry((s1, s2) -> s1.union(s2)); + return curry(Set::union); } /** @@ -444,7 +444,7 @@ public final Set intersect(final Set s) { * @see #intersect(Set) */ public static F, F, Set>> intersect() { - return curry((s1, s2) -> s1.intersect(s2)); + return curry(Set::intersect); } /** @@ -464,14 +464,14 @@ public final Set minus(final Set s) { * @see #minus(Set) */ public static F, F, Set>> minus() { - return curry((s1, s2) -> s1.minus(s2)); + return curry(Set::minus); } - public Option min() { + public final Option min() { return isEmpty() ? none() : l().min().orElse(some(head())); } - public Option max() { + public final Option max() { return isEmpty() ? none() : r().max().orElse(some(head())); } @@ -500,7 +500,7 @@ public final int size() { */ public final P3, Option, Set> split(final A a) { if (isEmpty()) - return P.p(empty(ord), Option.none(), empty(ord)); + return P.p(empty(ord), Option.none(), empty(ord)); else { final A h = head(); final Ordering i = ord.compare(a, h); @@ -515,6 +515,129 @@ public final P3, Option, Set> split(final A a) { } } + /** + * Find element equal to the given one. + * + * @param a An element to compare with. + * @return Some element in this set equal to the given one, or None. + */ + public final Option lookup(final A a) { + Set s = this; + while (true) + if (s.isEmpty()) + return none(); + else { + final A h = s.head(); + final Ordering i = ord.compare(a, h); + if (i == LT) + s = s.l(); + else if (i == GT) + s = s.r(); + else + return some(h); + } + } + + /** + * Find largest element smaller than the given one. + * + * @param a An element to compare with. + * @return Some largest element in this set smaller than the given one, or None. + */ + public final Option lookupLT(final A a) { + Set s = this; + Option r = none(); + while (true) + if (s.isEmpty()) + return r; + else { + final A h = s.head(); + final Ordering i = ord.compare(a, h); + if (i == GT) { + r = some(h); + s = s.r(); + } + else + s = s.l(); + } + } + + /** + * Find smallest element greater than the given one. + * + * @param a An element to compare with. + * @return Some smallest element in this set greater than the given one, or None. + */ + public final Option lookupGT(final A a) { + Set s = this; + Option r = none(); + while (true) + if (s.isEmpty()) + return r; + else { + final A h = s.head(); + final Ordering i = ord.compare(a, h); + if (i == LT) { + r = some(h); + s = s.l(); + } + else + s = s.r(); + } + } + + /** + * Find largest element smaller or equal to the given one. + * + * @param a An element to compare with. + * @return Some largest element in this set smaller or equal to the given one, or None. + */ + public final Option lookupLE(final A a) { + Set s = this; + Option r = none(); + while (true) + if (s.isEmpty()) + return r; + else { + final A h = s.head(); + final Ordering i = ord.compare(a, h); + if (i == LT) + s = s.l(); + else if (i == GT) { + r = some(h); + s = s.r(); + } + else + return some(h); + } + } + + /** + * Find smallest element greater or equal to the given one. + * + * @param a An element to compare with. + * @return Some smallest element in this set greater or equal to the given one, or None. + */ + public final Option lookupGE(final A a) { + Set s = this; + Option r = none(); + while (true) + if (s.isEmpty()) + return r; + else { + final A h = s.head(); + final Ordering i = ord.compare(a, h); + if (i == LT) { + r = some(h); + s = s.l(); + } + else if (i == GT) + s = s.r(); + else + return some(h); + } + } + /** * Returns true if this set is a subset of the given set. * @@ -539,7 +662,7 @@ public final boolean subsetOf(final Set s) { */ public static Set join(final Ord o, final Set> s) { final F, Set> id = identity(); - return s.foldMap(id, Monoid.setMonoid(o)); + return s.foldMap(id, Monoid.setMonoid(o)); } /** @@ -590,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/State.java b/core/src/main/java/fj/data/State.java index 46af5392..c5b638a3 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -1,120 +1,133 @@ package fj.data; -import fj.*; - -import java.util.*; +import fj.F; +import fj.P2; +import fj.Unit; +import fj.control.Trampoline; import static fj.P.p; - -/** - * Created by MarkPerry on 7/07/2014. - */ -public class State { - - private F> run; - - private State(F> f) { - run = f; - } - - public P2 run(S s) { - return run.f(s); - } - - public static State unit(F> f) { - return new State(f); - } - - public static State units(F f) { - return unit((S s) -> { - S s2 = f.f(s); - return p(s2, s2); - }); - } - - public static State constant(A a) { - return unit(s -> p(s, a)); - } - - public State map(F f) { - return unit((S s) -> { - P2 p2 = run(s); - B b = f.f(p2._2()); - return p(p2._1(), b); - }); - } - - public static State modify(F f) { - return State.init().flatMap(s -> unit(s2 -> p(f.f(s), Unit.unit()))); - } - - public State mapState(F, P2> f) { - return unit(s -> f.f(run(s))); - } - - public static State flatMap(State mb, F> f) { - return mb.flatMap(f); - } - - public State flatMap(F> f) { - return unit((S s) -> { - P2 p = run(s); - A a = p._2(); - S s2 = p._1(); - State smb = f.f(a); - return smb.run(s2); - }); - } - - public static State init() { - return unit(s -> p(s, s)); - } - - public State gets() { - return unit(s -> { - P2 p = run(s); - S s2 = p._1(); - return p(s2, s2); - }); - } - - public static State put(S s) { - return State.unit((S z) -> p(s, Unit.unit())); - } - - public A eval(S s) { - return run(s)._2(); - } - - public S exec(S s) { - return run(s)._1(); - } - - public State withs(F f) { - return unit(F1Functions.andThen(f, run)); - } - - public static State gets(F f) { - return State.init().map(s -> f.f(s)); - } - - /** - * Evaluate each action in the sequence from left to right, and collect the results. - */ - public static State> sequence(List> list) { - return list.foldLeft((State> acc, State ma) -> - acc.flatMap((List xs) -> ma.map((A x) -> xs.snoc(x)) - ), constant(List.nil())); - } - - /** - * Map each element of a structure to an action, evaluate these actions from left to right - * and collect the results. - */ - public static State> traverse(List list, F> f) { - return list.foldLeft((State> acc, A a) -> - acc.flatMap(bs -> f.f(a).map(b -> bs.snoc(b)) - ), constant(List.nil())); - } +import static fj.control.Trampoline.suspend; +import static fj.data.List.cons; + +public final class State { + + public static State unit(F> runF) { + return new State<>(s -> Trampoline.pure(runF.f(s))); + } + + public static State init() { + return unit(s -> dup(s)); + } + + public static State units(F f) { + return unit(s -> dup(f.f(s))); + } + + private static P2 dup(S s) { + return p(s, s); + } + + public static State constant(A a) { + return unit(s -> p(s, a)); + } + + public static State gets(F f) { + return unit(s -> p(s, f.f(s))); + } + + public static State put(S s) { + return unit(ignoredS -> p(s, Unit.unit())); + } + + public static State modify(F f) { + return unit(s -> p(f.f(s), Unit.unit())); + } + + public static State flatMap(State ts, F> f) { + return ts.flatMap(f); + } + + /** + * Evaluate each action in the sequence from left to right, and collect the results. + */ + public static State> sequence(List> list) { + return list + .foldLeft( + (acc, ts) -> acc.flatMap(as -> ts.map(a -> cons(a, as))), + State.>constant(List.nil())) + .map(as -> as.reverse()); + } + + /** + * Map each element of a structure to an action, evaluate these actions from left to right + * and collect the results. + */ + public static State> traverse(List list, F> f) { + return list + .foldLeft( + (acc, a) -> acc.flatMap(bs -> f.f(a).map(b -> cons(b, bs))), + State.>constant(List.nil())) + .map(bs -> bs.reverse()); + } + + private static State suspended(F>> runF) { + return new State<>(s -> suspend(() -> runF.f(s))); + } + + private final F>> runF; + + private State(F>> runF) { + this.runF = runF; + } + + public P2 run(S s) { + return runF.f(s).run(); + } + + public A eval(S s) { + return run(s)._2(); + } + + public S exec(S s) { + return run(s)._1(); + } + + public State gets() { + return mapState(result -> p(result._1(), result._1())); + } + + public State map(F f) { + return mapState(result -> p(result._1(), f.f(result._2()))); + } + + public State mapState(F, P2> f) { + return suspended(s -> runF.f(s).map(result -> f.f(result))); + } + + 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/main/java/fj/data/Stream.java b/core/src/main/java/fj/data/Stream.java index d21c2f15..8afa36ee 100644 --- a/core/src/main/java/fj/data/Stream.java +++ b/core/src/main/java/fj/data/Stream.java @@ -2,7 +2,9 @@ import fj.Equal; import fj.F0; +import fj.F3; import fj.Hash; +import fj.Semigroup; import fj.Show; import fj.F; import fj.F2; @@ -13,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; @@ -21,13 +24,10 @@ import java.util.*; import static fj.Bottom.error; -import static fj.Function.compose; -import static fj.Function.constant; -import static fj.Function.curry; -import static fj.Function.flip; -import static fj.Function.identity; +import static fj.Function.*; import static fj.P.p; import static fj.P.p2; +import static fj.P.weakMemo; import static fj.Unit.unit; import static fj.control.parallel.Promise.promise; import static fj.data.Array.mkArray; @@ -40,8 +40,6 @@ /** * A lazy (not yet evaluated), immutable, singly linked list. - * - * @version %build.number% */ public abstract class Stream implements Iterable { private Stream() { @@ -89,20 +87,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. * @@ -122,7 +106,7 @@ public final B uncons(final B nil, final F>, B>> cons) { * @return The final result after the right-fold reduction. */ public final B foldRight(final F, B>> f, final B b) { - return isEmpty() ? b : f.f(head()).f(P.lazy(() -> tail()._1().foldRight(f, b))); + return foldRight(uncurryF2(f), b); } /** @@ -133,7 +117,7 @@ public final B foldRight(final F, B>> f, final B b) { * @return The final result after the right-fold reduction. */ public final B foldRight(final F2, B> f, final B b) { - return foldRight(curry(f), b); + return isEmpty() ? b : f.f(head(), P.lazy(() -> tail()._1().foldRight(f, b))); } /** @@ -144,7 +128,7 @@ public final B foldRight(final F2, B> f, final B b) { * @return The final result after the right-fold reduction. */ public final B foldRight1(final F> f, final B b) { - return foldRight(compose(Function., B, B>andThen().f(P1.__1()), f), b); + return foldRight1(uncurryF2(f), b); } /** @@ -155,7 +139,7 @@ public final B foldRight1(final F> f, final B b) { * @return The final result after the right-fold reduction. */ public final B foldRight1(final F2 f, final B b) { - return foldRight1(curry(f), b); + return isEmpty() ? b : f.f(head(), tail()._1().foldRight1(f, b)); } /** @@ -166,12 +150,7 @@ public final B foldRight1(final F2 f, final B b) { * @return The final result after the left-fold reduction. */ public final B foldLeft(final F> f, final B b) { - B x = b; - - for (Stream xs = this; !xs.isEmpty(); xs = xs.tail()._1()) - x = f.f(x).f(xs.head()); - - return x; + return foldLeft(uncurryF2(f), b); } /** @@ -182,7 +161,12 @@ public final B foldLeft(final F> f, final B b) { * @return The final result after the left-fold reduction. */ public final B foldLeft(final F2 f, final B b) { - return foldLeft(curry(f), b); + B x = b; + + for (Stream xs = this; !xs.isEmpty(); xs = xs.tail()._1()) + x = f.f(x, xs.head()); + + return x; } /** @@ -193,7 +177,9 @@ public final B foldLeft(final F2 f, final B b) { * @return The final result after the left-fold reduction. */ public final A foldLeft1(final F2 f) { - return foldLeft1(curry(f)); + if (isEmpty()) + throw error("Undefined: foldLeft1 on empty list"); + return tail()._1().foldLeft(f, head()); } /** @@ -204,9 +190,7 @@ public final A foldLeft1(final F2 f) { * @return The final result after the left-fold reduction. */ public final A foldLeft1(final F> f) { - if (isEmpty()) - throw error("Undefined: foldLeft1 on empty list"); - return tail()._1().foldLeft(f, head()); + return foldLeft1(uncurryF2(f)); } /** @@ -254,7 +238,7 @@ public Stream prefix(final A x, final Stream xs) { * @return A new stream after the given function has been applied to each element. */ public final Stream map(final F f) { - return isEmpty() ? Stream.nil() : cons(f.f(head()), () -> tail()._1().map(f)); + return isEmpty() ? Stream.nil() : cons(f.f(head()), () -> tail()._1().map(f)); } /** @@ -351,8 +335,8 @@ public final Stream removeAll(final F f) { * and returns a stream of the results. */ public static F> sequence_(final Stream> fs) { - return fs.foldRight((baf, p1) -> Function.bind(baf, p1._1(), Function.curry((a, stream) -> cons(a, p(stream)))), Function - .>constant(Stream.nil())); + return fs.foldRight((baf, p1) -> Function.bind(baf, p1._1(), curry((a, stream) -> cons(a, p(stream)))), Function + .constant(Stream.nil())); } /** @@ -374,7 +358,7 @@ public final F> mapM(final F> f) { * @return A new stream after performing the map, then final join. */ public final Stream bind(final F> f) { - return foldRight(h -> (t -> f.f(h).append(t)), nil()); + return foldRight(h -> t -> f.f(h).append(t), nil()); } /** @@ -521,7 +505,7 @@ public final Stream sequence(final Stream bs) { * @return The stream of IOs after sequencing. */ public static Stream> sequence(final IO> io) { - return IOFunctions.runSafe(io).map(a -> IOFunctions.unit(a)); + return IOFunctions.runSafe(io).map(IOFunctions::unit); } /** @@ -531,7 +515,7 @@ public static Stream> sequence(final IO> io) { * @return The stream of (pre-calculated) lazy values after sequencing. */ public static Stream> sequence(final F0> p) { - return p.f().map(a -> P.p(a)); + return p.f().map(P::p); } /** @@ -541,7 +525,7 @@ public static Stream> sequence(final F0> p) { * @return The stream of options after sequencing. */ public static Stream> sequence(final Option> o) { - return o.isNone() ? Stream.nil() : o.some().map(a -> Option.some(a)); + return o.isNone() ? nil() : o.some().map(Option::some); } /** @@ -551,7 +535,7 @@ public static Stream> sequence(final Option> o) { * @return A new stream after applying the given stream of functions through this stream. */ public final Stream apply(final Stream> sf) { - return sf.bind(f -> map(f)); + return sf.bind(this::map); } /** @@ -566,9 +550,9 @@ public final Stream interleave(final Stream as) { public static Stream enumerationStream(Enumeration e) { if (e.hasMoreElements()) { - return Stream.cons(e.nextElement(), () -> enumerationStream(e)); + return cons(e.nextElement(), () -> enumerationStream(e)); } else { - return Stream.nil(); + return nil(); } } @@ -579,7 +563,7 @@ public static Stream enumerationStream(Enumeration e) { * @return A new stream with the elements of this stream sorted according to the given ordering. */ public final Stream sort(final Ord o) { - return mergesort(o, map(flip(Stream.cons()).f(p(Stream.nil())))); + return mergesort(o, map(flip(Stream.cons()).f(p(Stream.nil())))); } // Merges a stream of individually sorted streams into a single sorted stream. @@ -627,7 +611,7 @@ public final Stream sort(final Ord o, final Strategy s) { private Promise> qs(final Ord o, final Strategy s) { if (isEmpty()) - return promise(s, P.p(this)); + return promise(s, p(this)); else { final F id = identity(); final A x = head(); @@ -699,7 +683,7 @@ public int size() { * to value (exclusive). */ public static Stream range(final int from, final long to) { - return from >= to ? Stream.nil() : cons(from, () -> range(from + 1, to)); + return from >= to ? Stream.nil() : cons(from, () -> range(from + 1, to)); } /** @@ -712,26 +696,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. */ @@ -767,7 +731,7 @@ public static Stream forever(final Enumerator e, final A from) { * given value and stepping at the given increment. */ public static Stream forever(final Enumerator e, final A from, final long step) { - return cons(from, () -> e.plus(from, step).map(a -> forever(e, a, step)).orSome(Stream.nil())); + return cons(from, () -> e.plus(from, step).map(a -> forever(e, a, step)).orSome(Stream.nil())); } /** @@ -797,7 +761,7 @@ public static Stream range(final Enumerator e, final A from, final A t */ public static Stream range(final Enumerator e, final A from, final A to, final long step) { final Ordering o = e.order().compare(from, to); - return o == EQ || step > 0L && o == GT || step < 0L && o == LT ? single(from) : cons(from, () -> Stream.join(e.plus(from, step).filter(a -> !(o == LT + return o == EQ || step > 0L && o == GT || step < 0L && o == LT ? single(from) : cons(from, () -> join(e.plus(from, step).filter(a -> !(o == LT ? e.order().isLessThan(to, a) : e.order().isGreaterThan(to, a))).map(a1 -> range(e, a1, to, step)).toStream())); } @@ -829,7 +793,7 @@ public static F, F, Stream>> filter() { * @return A new stream with a length the same as the shortest of this stream and the given stream. */ public final Stream zapp(final Stream> fs) { - return fs.isEmpty() || isEmpty() ? Stream.nil() : + return fs.isEmpty() || isEmpty() ? Stream.nil() : cons(fs.head().f(head()), () -> tail()._1().zapp(fs.tail()._1())); } @@ -892,7 +856,7 @@ public final Stream> zip(final Stream bs) { * @return A new stream with the same length as this stream. */ public final Stream> zipIndex() { - return zipWith(range(0), (a, i) -> p(a, i)); + return zipWith(range(0), (F2>) P::p); } /** @@ -903,7 +867,7 @@ public final Stream> zipIndex() { * @return An either projection of this stream. */ public final Either toEither(final F0 x) { - return isEmpty() ? Either.left(x.f()) : Either.right(head()); + return isEmpty() ? Either.left(x.f()) : Either.right(head()); } /** @@ -913,18 +877,7 @@ public final Either toEither(final F0 x) { * @return An option projection of this stream. */ public final Option toOption() { - return isEmpty() ? Option.none() : some(head()); - } - - @SuppressWarnings({"unchecked", "UnnecessaryFullyQualifiedName"}) - public final A[] toJavaArray() { - final A[] array = (A[]) new Object[length()]; - int i = 0; - for (A a: this) { - array[i] = a; - i++; - } - return array; + return isEmpty() ? Option.none() : some(head()); } /** @@ -934,7 +887,7 @@ public final A[] toJavaArray() { */ public final List toList() { List.Buffer buf = List.Buffer.empty(); - foreachDoEffect(a -> buf.snoc(a)); + foreachDoEffect(buf::snoc); return buf.toList(); } @@ -942,7 +895,7 @@ public final List toList() { * Returns a java.util.List projection of this stream. */ public final java.util.List toJavaList() { - return new java.util.LinkedList(toCollection()); + return new java.util.LinkedList<>(toCollection()); } /** @@ -950,7 +903,7 @@ public final java.util.List toJavaList() { * * @return A array projection of this stream. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final Array toArray() { final int l = length(); final Object[] a = new Object[l]; @@ -999,7 +952,7 @@ public final A[] array(final Class c) { * @return A new stream with the given element at the head. */ public final Stream cons(final A a) { - return new Cons(a, () -> Stream.this); + return new Cons<>(a, () -> Stream.this); } /** @@ -1011,7 +964,7 @@ public final Stream cons(final A a) { */ public static String asString(final Stream cs) { StringBuilder sb = new StringBuilder(); - cs.foreachDoEffect(c -> sb.append(c)); + cs.foreachDoEffect(sb::append); return sb.toString(); } @@ -1054,8 +1007,8 @@ public final Stream snoc(final F0 a) { */ public final Stream take(final int n) { return n <= 0 || isEmpty() ? - Stream.nil() : - cons(head(), () -> n <= 1 ? Stream.nil() : tail()._1().take(n - 1)); + Stream.nil() : + cons(head(), () -> n <= 1 ? Stream.nil() : tail()._1().take(n - 1)); } /** @@ -1065,11 +1018,9 @@ public final Stream take(final int n) { * @return A stream with a length the same, or less than, this stream. */ public final Stream drop(final int i) { - int c = 0; - Stream xs = this; - for (; xs.isNotEmpty() && c < i; xs = xs.tail()._1()) + for (int c = 0; xs.isNotEmpty() && c < i; xs = xs.tail()._1()) c++; return xs; @@ -1087,29 +1038,7 @@ public final Stream takeWhile(final F f) { this : f.f(head()) ? cons(head(), () -> tail()._1().takeWhile(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), b -> - bs.cons(b))), 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(b -> bs.cons(b))), some(Stream.nil())); + Stream.nil(); } /** @@ -1141,11 +1070,11 @@ public final P2, Stream> span(final F p) { else if (p.f(head())) { final P1, Stream>> yszs = P.lazy(() -> tail()._1().span(p)); return P.lazy( - () -> cons(head(), yszs.map(P2., Stream>__1())), + () -> cons(head(), yszs.map(P2.__1())), () -> yszs._1()._2() ); } else - return p(Stream.nil(), this); + return p(Stream.nil(), this); } /** @@ -1182,7 +1111,7 @@ public final P2, Stream> split(final F p) { * @return A new stream that is the reverse of this one. */ public final Stream reverse() { - return foldLeft(as -> a -> cons(a, () -> as), Stream.nil()); + return foldLeft((as, a) -> cons(a, () -> as), Stream.nil()); } /** @@ -1253,25 +1182,25 @@ public final boolean forall(final F f) { } @Override - public boolean equals(Object other) { - return Equal.equals0(Stream.class, this, other, () -> Equal.streamEqual(Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(Stream.class, this, other, () -> Equal.streamEqual(Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.streamHash(Hash.anyHash()).hash(this); } @Override - public String toString() { + public final String toString() { return toStringLazy(); } - public String toStringLazy() { + public final String toStringLazy() { return isEmpty() ? "Nil()" : "Cons(" + Show.anyShow().showS(head()) + ", ?)"; } - public String toStringEager() { + public final String toStringEager() { return Show.streamShow(Show.anyShow()).showS(this); } @@ -1320,7 +1249,7 @@ public final Stream cobind(final F, B> k) { * @return a stream of the suffixes of this stream, starting with the stream itself. */ public final Stream> tails() { - return isEmpty() ? Stream.>nil() : cons(this, () -> tail()._1().tails()); + return isEmpty() ? Stream.nil() : cons(this, () -> tail()._1().tails()); } /** @@ -1329,7 +1258,7 @@ public final Stream> tails() { * @return a stream of the prefixes of this stream, starting with the stream itself. */ public final Stream> inits() { - final Stream> nil = Stream.cons(Stream.nil(), () -> nil()); + final Stream> nil = cons(Stream.nil(), Stream::nil); return isEmpty() ? nil : nil.append(() -> tail()._1().inits().map(Stream.cons_().f(head()))); } @@ -1339,7 +1268,7 @@ public final Stream> inits() { * @return a stream of the infixes of this stream. */ public final Stream> substreams() { - return tails().bind(stream -> stream.inits()); + return tails().bind(Stream::inits); } /** @@ -1349,7 +1278,7 @@ public final Stream> substreams() { * @return the position of the first element matching the given predicate, if any. */ public final Option indexOf(final F p) { - return zipIndex().find(p2 -> p.f(p2._1())).map(P2.__2()); + return zipIndex().find(p2 -> p.f(p2._1())).map(P2.__2()); } /** @@ -1360,7 +1289,7 @@ public final Option indexOf(final F p) { */ public final Stream sequenceW(final Stream, B>> fs) { return fs.isEmpty() - ? Stream.nil() + ? Stream.nil() : cons(fs.head().f(this), () -> sequenceW(fs.tail()._1())); } @@ -1370,7 +1299,7 @@ public final Stream sequenceW(final Stream, B>> fs) { * @return A function from natural numbers to values with the corresponding position in this stream. */ public final F toFunction() { - return i -> index(i); + return this::index; } /** @@ -1398,7 +1327,7 @@ public static Stream fromFunction(final Enumerator e, final F final Option s = e.successor(i); return s.isSome() ? fromFunction(e, f, s.some()) - : Stream.nil(); + : Stream.nil(); }); } @@ -1411,8 +1340,8 @@ public static Stream fromFunction(final Enumerator e, final F public static P2, Stream> unzip(final Stream> xs) { return xs.foldRight((p, ps) -> { final P2, Stream> pp = ps._1(); - return P.p(cons(p._1(), P.p(pp._1())), cons(p._2(), P.p(pp._2()))); - }, P.p(Stream.nil(), Stream.nil())); + return p(cons(p._1(), p(pp._1())), cons(p._2(), p(pp._2()))); + }, p(Stream.nil(), Stream.nil())); } /** @@ -1421,7 +1350,7 @@ public static P2, Stream> unzip(final Stream> xs) { * @return a function that zips two given streams with a given function. */ public static F, F, F>, Stream>>> zipWith() { - return curry((as, bs, f) -> as.zipWith(bs, f)); + return curry((F3, Stream, F>, Stream>) Stream::zipWith); } private static final class Nil extends Stream { @@ -1440,7 +1369,7 @@ private static final class Cons extends Stream { Cons(final A head, final F0> tail) { this.head = head; - this.tail = P1.memo(tail); + this.tail = weakMemo(tail); } public A head() { @@ -1477,7 +1406,7 @@ public static F, Stream>> cons_() { * @return An empty stream. */ public static Stream nil() { - return new Nil(); + return new Nil<>(); } /** @@ -1486,7 +1415,7 @@ public static Stream nil() { * @return An empty stream. */ public static P1> nil_() { - return P.lazy(() -> new Nil()); + return P.lazy(Nil::new); } /** @@ -1495,7 +1424,7 @@ public static P1> nil_() { * @return A function that determines whether a given stream is empty. */ public static F, Boolean> isEmpty_() { - return as -> as.isEmpty(); + return Stream::isEmpty; } /** @@ -1504,7 +1433,7 @@ public static F, Boolean> isEmpty_() { * @return A function that determines whether a given stream is not empty. */ public static F, Boolean> isNotEmpty_() { - return as -> as.isNotEmpty(); + return Stream::isNotEmpty; } /** @@ -1514,7 +1443,7 @@ public static F, Boolean> isNotEmpty_() { * @return A stream of one element containing the given value. */ public static Stream single(final A a) { - return cons(a, () -> nil()); + return cons(a, Stream::nil); } /** @@ -1523,7 +1452,7 @@ public static Stream single(final A a) { * @return a function that yields a stream containing its argument. */ public static F> single() { - return a -> single(a); + return Stream::single; } /** @@ -1534,7 +1463,7 @@ public static F> single() { * @return The stream with the given element prepended. */ public static Stream cons(final A head, final F0> tail) { - return new Cons(head, tail); + return new Cons<>(head, tail); } /** @@ -1551,7 +1480,7 @@ public static Stream cons(final A head, final F0> tail) { * @return A function that joins a stream of streams using a bind operation. */ public static F>, Stream> join() { - return as -> join(as); + return Stream::join; } /** @@ -1584,7 +1513,7 @@ public static Stream unfold(final F>> f, final B b) */ public static Stream iterateWhile(final F f, final F p, final A a) { return unfold( - o -> Option.iif(p2 -> p.f(o), P.p(o, f.f(o))) + o -> Option.iif(p2 -> p.f(o), p(o, f.f(o))) , a); } @@ -1600,9 +1529,9 @@ public static Stream iterableStream(final Iterable i) { @SafeVarargs public static Stream arrayStream(final A...as) { - return as.length == 0 ? Stream.nil() - : unfold(P2.tuple((as1, i) -> i >= as.length ? Option.>>none() - : some(P.p(as[i], P.p(as, i + 1)))), P.p(as, 0)); + return as.length == 0 ? Stream.nil() + : unfold(P2.tuple((as1, i) -> i >= as.length ? Option.none() + : some(p(as[i], p(as, i + 1)))), p(as, 0)); } /** @@ -1646,7 +1575,7 @@ public static Stream iterate(final F f, final A a) { * starting at a given value. */ public static F, F>> iterate() { - return curry((f, a) -> iterate(f, a)); + return curry(Stream::iterate); } /** @@ -1666,4 +1595,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/main/java/fj/data/Tree.java b/core/src/main/java/fj/data/Tree.java index f02a6e73..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 { /** @@ -38,7 +36,7 @@ private Tree(final A root, final P1>> subForest) { * @return A nullary tree with the root element in it. */ public static Tree leaf(final A root) { - return node(root, Stream.>nil()); + return node(root, Stream.nil()); } /** @@ -49,7 +47,7 @@ public static Tree leaf(final A root) { * @return A newly sprouted tree. */ public static Tree node(final A root, final P1>> forest) { - return new Tree(root, forest); + return new Tree<>(root, forest); } /** @@ -60,7 +58,7 @@ public static Tree node(final A root, final P1>> forest) { * @return A newly sprouted tree. */ public static Tree node(final A root, final Stream> forest) { - return new Tree(root, P.p(forest)); + return new Tree<>(root, P.p(forest)); } /** @@ -107,7 +105,7 @@ public P1>> subForest() { * @return A transformation from a tree to its root. */ public static F, A> root_() { - return a -> a.root(); + return Tree::root; } /** @@ -116,7 +114,7 @@ public static F, A> root_() { * @return A transformation from a tree to its subforest. */ public static F, P1>>> subForest_() { - return a -> a.subForest(); + return Tree::subForest; } /** @@ -127,22 +125,24 @@ 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())); + return squish.f(this, P.p(Stream.nil())); } /** + *
    {@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. * * @return The elements of the tree in pre-order. */ public static
    F, Stream> flatten_() { - return t -> t.flatten(); + return Tree::flatten; } /** @@ -152,9 +152,9 @@ public static F, Stream> flatten_() { */ public Stream> levels() { final F>, Stream>> flatSubForests = - Stream., Tree>bind_().f(compose(P1.>>__1(), Tree.subForest_())); - final F>, Stream> roots = Stream., A>map_().f(Tree.root_()); - return iterateWhile(flatSubForests, Stream.>isNotEmpty_(), single(this)).map(roots); + Stream., Tree>bind_().f(compose(P1.__1(), Tree.subForest_())); + final F>, Stream> roots = Stream., A>map_().f(Tree.root_()); + return iterateWhile(flatSubForests, Stream.isNotEmpty_(), single(this)).map(roots); } /** @@ -245,14 +245,14 @@ public Tree> cojoin() { } private static Stream drawSubTrees(final Show s, final Stream> ts) { - return ts.isEmpty() ? Stream.nil() + return ts.isEmpty() ? Stream.nil() : ts.tail()._1().isEmpty() ? shift("`- ", " ", ts.head().drawTree(s)).cons("|") : shift("+- ", "| ", ts.head().drawTree(s)) .append(drawSubTrees(s, ts.tail()._1())); } private static Stream shift(final String f, final String o, final Stream s) { - return Stream.repeat(o).cons(f).zipWith(s, Monoid.stringMonoid.sum()); + return repeat(o).cons(f).zipWith(s, Monoid.stringMonoid.sum()); } private Stream drawTree(final Show s) { @@ -261,7 +261,7 @@ private Stream drawTree(final Show s) { @Override public boolean equals(Object other) { - return Equal.equals0(Tree.class, this, other, () -> Equal.treeEqual(Equal.anyEqual())); + return Equal.equals0(Tree.class, this, other, () -> Equal.treeEqual(Equal.anyEqual())); } @Override @@ -303,7 +303,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); } /** @@ -328,14 +328,14 @@ public Tree zipWith(final Tree bs, final F> f) { public static Tree bottomUp(Tree t, final F>, B> f) { final F, Tree> recursiveCall = a -> bottomUp(a, f); final Stream> tbs = t.subForest()._1().map(recursiveCall); - return Tree.node(f.f(P.p(t.root(), tbs.map(Tree. getRoot()))), tbs); + return node(f.f(P.p(t.root(), tbs.map(Tree.getRoot()))), tbs); } /** * @return a function getting the root of a Tree */ private static F, A> getRoot() { - return a -> a.root(); + return Tree::root; } public boolean isLeaf() { @@ -343,7 +343,7 @@ public boolean isLeaf() { } public int length() { - return 1 + subForest._1().map(t -> t.length()).foldLeft((acc, i) -> acc + i, 0); + 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/TreeMap.java b/core/src/main/java/fj/data/TreeMap.java index 307c279e..e1af4d91 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; @@ -31,7 +30,7 @@ private TreeMap(final Set>> tree) { } private static Ord> ord(final Ord keyOrd) { - return keyOrd.contramap(P2.__1()); + return keyOrd.contramap(P2.__1()); } /** @@ -41,12 +40,12 @@ private static Ord> ord(final Ord keyOrd) { * @return an empty TreeMap with the given key order. */ public static TreeMap empty(final Ord keyOrd) { - return new TreeMap(Set.empty(TreeMap.>ord(keyOrd))); + return new TreeMap<>(Set.empty(TreeMap.ord(keyOrd))); } @Override public boolean equals(Object other) { - return Equal.equals0(TreeMap.class, this, other, () -> Equal.treeMapEqual(Equal.anyEqual(), Equal.anyEqual())); + return Equal.equals0(TreeMap.class, this, other, () -> Equal.treeMapEqual(Equal.anyEqual(), Equal.anyEqual())); } @Override @@ -70,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. * @@ -129,8 +114,7 @@ public static TreeMap arrayTreeMap(final Ord keyOrd, final P2 get(final K k) { - final Option>> x = tree.split(P.p(k, Option.none()))._2(); - return x.bind(P2.>__2()); + return tree.lookup(p(k, Option.none())).bind(P2::_2); } /** @@ -142,7 +126,7 @@ public Option get(final K k) { * @return A new tree map with the given value mapped to the given key. */ public TreeMap set(final K k, final V v) { - return new TreeMap(tree.insert(P.p(k, Option.some(v)))); + return new TreeMap<>(tree.insert(p(k, Option.some(v)))); } /** @@ -152,7 +136,7 @@ public TreeMap set(final K k, final V v) { * @return A new tree map with the entry corresponding to the given key removed. */ public TreeMap delete(final K k) { - return new TreeMap(tree.delete(P.p(k, Option.none()))); + return new TreeMap<>(tree.delete(p(k, Option.none()))); } /** @@ -179,7 +163,7 @@ public boolean isEmpty() { * @return All values in this tree map. */ public List values() { - return iterableList(join(tree.toList().map(compose(IterableW.>wrap(), P2.>__2())))); + return iterableList(join(tree.toList().map(compose(IterableW.wrap(), P2.__2())))); } /** @@ -188,7 +172,7 @@ public List values() { * @return All keys in this tree map. */ public List keys() { - return tree.toList().map(P2.>__1()); + return tree.toList().map(P2.__1()); } /** @@ -198,7 +182,7 @@ public List keys() { * @return true if this tree map contains the given key, false otherwise. */ public boolean contains(final K k) { - return tree.member(P.p(k, Option.none())); + return tree.member(p(k, Option.none())); } /** @@ -208,8 +192,8 @@ public boolean contains(final K k) { * @return A iterator for this map's key-value pairs. */ public Iterator> iterator() { - return join(tree.toStream().map(P2., IterableW>map2_(IterableW.>wrap()) - ).map(P2.tuple(compose(IterableW.>map(), P.p2())))).iterator(); + return join(tree.toStream().map(P2.map2_(IterableW.wrap()) + ).map(P2.tuple(compose(IterableW.map(), P.p2())))).iterator(); } /** @@ -218,9 +202,9 @@ public Iterator> iterator() { * @return A new mutable map isomorphic to this tree map. */ public Map toMutableMap() { - final F>> fakePair = k -> P.p(k, Option.none()); + final F>> fakePair = k -> p(k, Option.none()); final Comparator comparator = tree.ord().contramap(fakePair).toComparator(); - final Map m = new java.util.TreeMap(comparator); + final Map m = new java.util.TreeMap<>(comparator); for (final P2 e : this) { m.put(e._1(), e._2()); } @@ -264,7 +248,7 @@ public static TreeMap fromMutableMap(final Ord ord, final Map> get() { - return k -> get(k); + return this::get; } /** @@ -277,8 +261,8 @@ public F> get() { */ public P2> update(final K k, final F f) { final P2>>> up = - tree.update(p(k, Option.none()), compose(P2.tuple(P.p2()), P2., Option>map2_(Option.map().f(f)))); - return P.p(up._1(), new TreeMap(up._2())); + tree.update(p(k, Option.none()), compose(P2.tuple(P.p2()), P2.map2_(Option.map().f(f)))); + return p(up._1(), new TreeMap<>(up._2())); } /** @@ -309,19 +293,19 @@ 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); - return tree.split(p(k, Option.none())).map1(getSome).map3(getSome) - .map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.>__2()))); + final F>>, Set> getSome = Option.fromSome().o(P2.>__2()).mapSet(ord); + return tree.split(p(k, Option.none())).map1(getSome).map3(getSome) + .map2(Option.join().o(P2.>__2().mapOption())); } /** - * 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) { - TreeMap empty = TreeMap.empty(ord); + public static TreeMap setTreeMap(Ord ord, Set>> s) { + TreeMap empty = TreeMap.empty(ord); TreeMap tree = s.toList().foldLeft((tm, p2) -> { Option opt = p2._2(); if (opt.isSome()) { @@ -347,9 +331,9 @@ private static TreeMap treeMap(Ord ord, Set>> s) * given key if present, otherwise None. */ public P3, Option, TreeMap> splitLookup(final K k) { - P3>>, Option>>, Set>>> p3 = tree.split(P.p(k, get(k))); - Ord o = tree.ord().contramap(k2 -> P.p(k2, Option.none())); - return P.p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3())); + P3>>, Option>>, Set>>> p3 = tree.split(p(k, get(k))); + Ord o = tree.ord().contramap(k2 -> p(k2, Option.none())); + return p(setTreeMap(o, p3._1()), get(k), setTreeMap(o, p3._3())); } /** @@ -358,12 +342,12 @@ public P3, Option, TreeMap> splitLookup(final K k) { * @param f A function to apply to the values of this TreeMap. * @return A new TreeMap with the values transformed by the given function. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public TreeMap map(final F f) { - final F>, P2>> g = P2.map2_(F1Functions.mapOption(f)); - final F>> coord = flip(P.>p2()).f(Option.none()); + 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)); + return new TreeMap<>(tree.map(TreeMap.ord(o), g)); } /** @@ -377,7 +361,7 @@ public Option> min() { * Returns the minimum key in the tree if the tree is not empty. */ public Option minKey() { - return tree.min().map(p -> p._1()); + return tree.min().map(P2::_1); } /** @@ -391,7 +375,7 @@ public Option> max() { * Returns the maximum key in the tree if the tree is not empty. */ public Option maxKey() { - return tree.max().map(p -> p._1()); + return tree.max().map(P2::_1); } /** diff --git a/core/src/main/java/fj/data/TreeZipper.java b/core/src/main/java/fj/data/TreeZipper.java index 959de5a7..545238f3 100644 --- a/core/src/main/java/fj/data/TreeZipper.java +++ b/core/src/main/java/fj/data/TreeZipper.java @@ -1,679 +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; - } - - /** - * 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( - (tree, lefts, rights, parents) -> treeZipper(tree, lefts, rights, parents)); - } - - /** - * 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 a -> a.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 a -> a.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.>, Tree, Stream>>>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., Stream>>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; - } - - /** - * 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 t -> fromTree(t); - } - - /** - * 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 z -> z.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 z -> z.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( - new F>, - Option>>, TreeZipper, Stream>>>, - Option>>>>() { - public Option>>, TreeZipper, Stream>>>, - Option>>> f(final Option> o) { - Option>>, TreeZipper, Stream>>>, - Option>>> r = none(); - for (final TreeZipper z : o) { - r = some(P.p(P.p(z.uf(TreeZipper.left_()), z, z.uf(TreeZipper.right_())), z.parent())); - } - return r; - } - }, 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() { - return tz -> P., P1>>>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()); - } - }); - } - - /** - * 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/Validation.java b/core/src/main/java/fj/data/Validation.java index 3a2cfa23..5c1985a4 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -1,23 +1,22 @@ 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 * values - * - * @version %build.number% */ public class Validation implements Iterable { private final Either e; @@ -31,7 +30,7 @@ protected Validation(final Either e) { * * @return true if this is a failure, false otherwise. */ - public boolean isFail() { + public final boolean isFail() { return e.isLeft(); } @@ -40,7 +39,7 @@ public boolean isFail() { * * @return true if this is a success, false otherwise. */ - public boolean isSuccess() { + public final boolean isSuccess() { return e.isRight(); } @@ -49,7 +48,7 @@ public boolean isSuccess() { * * @return the failing value, or throws an error if there is no failing value. */ - public E fail() { + public final E fail() { if (isFail()) return e.left().value(); else @@ -61,7 +60,7 @@ public E fail() { * * @return the success value, or throws an error if there is no success value. */ - public T success() { + public final T success() { if (isSuccess()) return e.right().value(); else @@ -75,7 +74,7 @@ public T success() { * @param success The function to call if this succeeded. * @return The reduced value. */ - public X validation(final F fail, final F success) { + public final X validation(final F fail, final F success) { return e.either(fail, success); } @@ -84,7 +83,7 @@ public X validation(final F fail, final F success) { * * @return a failing projection of this validation. */ - public FailProjection f() { + public final FailProjection f() { return new FailProjection<>(this); } @@ -93,7 +92,7 @@ public FailProjection f() { * * @return An either projection of this validation. */ - public Either toEither() { + public final Either toEither() { return e; } @@ -103,7 +102,7 @@ public Either toEither() { * @param err The error message to fail with. * @return The success value. */ - public T successE(final F0 err) { + public final T successE(final F0 err) { return e.right().valueE(err); } @@ -113,7 +112,7 @@ public T successE(final F0 err) { * @param err The error message to fail with. * @return The success value. */ - public T successE(final String err) { + public final T successE(final String err) { return e.right().valueE(p(err)); } @@ -123,7 +122,7 @@ public T successE(final String err) { * @param t The value to return if this is failure. * @return The success value or the given value. */ - public T orSuccess(final F0 t) { + public final T orSuccess(final F0 t) { return e.right().orValue(t); } @@ -133,7 +132,7 @@ public T orSuccess(final F0 t) { * @param t The value to return if this is failure. * @return The success value or the given value. */ - public T orSuccess(final T t) { + public final T orSuccess(final T t) { return e.right().orValue(p(t)); } @@ -143,7 +142,7 @@ public T orSuccess(final T t) { * @param f The function to execute on the failing value. * @return The success value or the application of the given function to the failing value. */ - public T on(final F f) { + public final T on(final F f) { return e.right().on(f); } @@ -153,7 +152,7 @@ public T on(final F f) { * @param f The side-effect to execute. * @return The unit value. */ - public Unit foreach(final F f) { + public final Unit foreach(final F f) { return e.right().foreach(f); } @@ -162,7 +161,7 @@ public Unit foreach(final F f) { * * @param f The side-effect to execute. */ - public void foreachDoEffect(final Effect1 f) { + public final void foreachDoEffect(final Effect1 f) { e.right().foreachDoEffect(f); } @@ -172,11 +171,11 @@ public void foreachDoEffect(final Effect1 f) { * @param f The function to map. * @return A new validation with the function mapped. */ - @SuppressWarnings({"unchecked"}) - public Validation map(final F f) { + @SuppressWarnings("unchecked") + public final Validation map(final F f) { return isFail() ? - Validation.fail(fail()) : - Validation.success(f.f(success())); + Validation.fail(fail()) : + Validation.success(f.f(success())); } /** @@ -185,9 +184,9 @@ public Validation map(final F f) { * @param f The function to bind across this validation. * @return A new validation value after binding. */ - @SuppressWarnings({"unchecked"}) - public Validation bind(final F> f) { - return isSuccess() ? f.f(success()) : Validation.fail(fail()); + @SuppressWarnings("unchecked") + public final Validation bind(final F> f) { + return isSuccess() ? f.f(success()) : Validation.fail(fail()); } /** @@ -196,8 +195,8 @@ public Validation bind(final F> f) { * @param v The value to bind with. * @return A validation after binding. */ - public Validation sequence(final Validation v) { - return bind(Function.>constant(v)); + public final Validation sequence(final Validation v) { + return bind(Function.constant(v)); } /** @@ -205,10 +204,10 @@ public Validation sequence(final Validation v) { * all the failures using the semigroup, otherwise returns the successful list. */ public static Validation> sequence(final Semigroup s, final List> list) { - if (list.exists(v -> v.isFail())) { - return Validation.>fail(list.filter(v -> v.isFail()).map(v -> v.fail()).foldLeft1((e1, e2) -> s.sum(e1, e2))); + if (list.exists(Validation::isFail)) { + return Validation.fail(list.filter(Validation::isFail).map(v -> v.fail()).foldLeft1((F2) s::sum)); } else { - return Validation.success(list.foldLeft((List acc, Validation v) -> acc.cons(v.success()), List.nil()).reverse()); + return success(list.foldLeft((List acc, Validation v) -> acc.cons(v.success()), List.nil()).reverse()); } } @@ -220,8 +219,8 @@ public static Validation> sequence(final Semigroup s, final * @return None if this is a failure or if the given predicate p does not hold for the * success value, otherwise, returns a success in Some. */ - public Option> filter(final F f) { - return e.right().filter(f).map(Validation.validation()); + public final Option> filter(final F f) { + return e.right().filter(f).map(Validation.validation()); } /** @@ -230,7 +229,7 @@ public Option> filter(final F f) { * @param v The validation of the function to apply on the success value. * @return The result of function application in validation. */ - public Validation apply(final Validation> v) { + public final Validation apply(final Validation> v) { return v.bind(this::map); } @@ -242,7 +241,7 @@ public Validation apply(final Validation> v) { * @return true if this is a failure or returns the result of the application of the given * function to the success value. */ - public boolean forall(final F f) { + public final boolean forall(final F f) { return e.right().forall(f); } @@ -254,17 +253,17 @@ public boolean forall(final F f) { * @return false if this is a failure or returns the result of the application of the given * function to the success value. */ - public boolean exists(final F f) { + public final boolean exists(final F f) { return e.right().exists(f); } @Override - public boolean equals(Object other) { - return Equal.equals0(Validation.class, this, other, () -> Equal.validationEqual(Equal.anyEqual(), Equal.anyEqual())); + public final boolean equals(Object other) { + return Equal.equals0(Validation.class, this, other, () -> Equal.validationEqual(Equal.anyEqual(), Equal.anyEqual())); } @Override - public int hashCode() { + public final int hashCode() { return Hash.validationHash(Hash.anyHash(), Hash.anyHash()).hash(this); } @@ -273,7 +272,7 @@ public int hashCode() { * * @return A single element list if this is a success value, otherwise an empty list. */ - public List toList() { + public final List toList() { return e.right().toList(); } @@ -282,7 +281,7 @@ public List toList() { * * @return The success value in Some if there is one, otherwise None. */ - public Option toOption() { + public final Option toOption() { return e.right().toOption(); } @@ -291,7 +290,7 @@ public Option toOption() { * * @return A single element array if this is a success value, otherwise an empty list. */ - public Array toArray() { + public final Array toArray() { return e.right().toArray(); } @@ -300,7 +299,7 @@ public Array toArray() { * * @return A single element stream if this is a success value, otherwise an empty list. */ - public Stream toStream() { + public final Stream toStream() { return e.right().toStream(); } @@ -313,15 +312,15 @@ public Stream toStream() { * @return A failing validation if this or the given validation failed (with errors accumulated if both) or a * succeeding validation if both succeeded. */ - @SuppressWarnings({"unchecked"}) - public Validation accumapply(final Semigroup s, final Validation> v) { + @SuppressWarnings("unchecked") + public final Validation accumapply(final Semigroup s, final Validation> v) { return isFail() ? - Validation.fail(v.isFail() ? + Validation.fail(v.isFail() ? s.sum(v.fail(), fail()) : fail()) : v.isFail() ? - Validation.fail(v.fail()) : - Validation.success(v.success().f(success())); + Validation.fail(v.fail()) : + Validation.success(v.success().f(success())); } /** @@ -334,7 +333,7 @@ public Validation accumapply(final Semigroup s, final Validation Validation accumulate(final Semigroup s, final Validation va, final F> f) { + public final Validation accumulate(final Semigroup s, final Validation va, final F> f) { return va.accumapply(s, map(f)); } @@ -348,7 +347,7 @@ public Validation accumulate(final Semigroup s, final Validation * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, final F2 f) { + public final Validation accumulate(final Semigroup s, final Validation va, final F2 f) { return va.accumapply(s, map(curry(f))); } @@ -360,7 +359,7 @@ public Validation accumulate(final Semigroup s, final Validation * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va) { + public final Option accumulate(final Semigroup s, final Validation va) { return accumulate(s, va, (t, a) -> unit()).f().toOption(); } @@ -375,8 +374,8 @@ public Option accumulate(final Semigroup s, final Validation va) * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final F>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final F>> f) { return vb.accumapply(s, accumulate(s, va, f)); } @@ -391,8 +390,8 @@ public Validation accumulate(final Semigroup s, final Validat * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final F3 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final F3 f) { return vb.accumapply(s, accumulate(s, va, curry(f))); } @@ -405,7 +404,7 @@ public Validation accumulate(final Semigroup s, final Validat * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, final Validation vb) { + public final Option accumulate(final Semigroup s, final Validation va, final Validation vb) { return accumulate(s, va, vb, (t, a, b) -> unit()).f().toOption(); } @@ -421,9 +420,9 @@ public Option accumulate(final Semigroup s, final Validation * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final F>>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final F>>> f) { return vc.accumapply(s, accumulate(s, va, vb, f)); } @@ -439,9 +438,9 @@ public Validation accumulate(final Semigroup s, final Vali * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final F4 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final F4 f) { return vc.accumapply(s, accumulate(s, va, vb, curry(f))); } @@ -455,8 +454,8 @@ public Validation accumulate(final Semigroup s, final Vali * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, final Validation vb, - final Validation vc) { + public final Option accumulate(final Semigroup s, final Validation va, final Validation vb, + final Validation vc) { return accumulate(s, va, vb, vc, (t, a, b, c) -> unit()).f().toOption(); } @@ -473,10 +472,10 @@ public Option accumulate(final Semigroup s, final Validation Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, - final F>>>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, + final F>>>> f) { return vd.accumapply(s, accumulate(s, va, vb, vc, f)); } @@ -493,9 +492,9 @@ public Option accumulate(final Semigroup s, final Validation Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final F5 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final F5 f) { return vd.accumapply(s, accumulate(s, va, vb, vc, curry(f))); } @@ -510,8 +509,8 @@ public Option accumulate(final Semigroup s, final ValidationSome if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, final Validation vb, - final Validation vc, final Validation vd) { + public final Option accumulate(final Semigroup s, final Validation va, final Validation vb, + final Validation vc, final Validation vd) { return accumulate(s, va, vb, vc, vd, (t, a, b, c, d) -> unit()).f().toOption(); } @@ -529,10 +528,10 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final F>>>>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final F>>>>> f) { return ve.accumapply(s, accumulate(s, va, vb, vc, vd, f)); } @@ -550,10 +549,10 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final F6 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final F6 f) { return ve.accumapply(s, accumulate(s, va, vb, vc, vd, curry(f))); } @@ -569,9 +568,9 @@ public Option accumulate(final Semigroup s, final Validation< * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve) { + public final Option accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve) { return accumulate(s, va, vb, vc, vd, ve, (t, a, b, c, d, e1) -> unit()).f().toOption(); } @@ -590,11 +589,11 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf, - final F>>>>>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf, + final F>>>>>> f) { return vf.accumapply(s, accumulate(s, va, vb, vc, vd, ve, f)); } @@ -613,11 +612,11 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf, - final F7 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf, + final F7 f) { return vf.accumapply(s, accumulate(s, va, vb, vc, vd, ve, curry(f))); } @@ -634,10 +633,10 @@ public Option accumulate(final Semigroup s, final Validation< * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf) { + public final Option accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf) { return accumulate(s, va, vb, vc, vd, ve, vf, (t, a, b, c, d, e1, f) -> unit()).f().toOption(); } @@ -657,11 +656,11 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf, final Validation vg, - final F>>>>>>> f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf, final Validation vg, + final F>>>>>>> f) { return vg.accumapply(s, accumulate(s, va, vb, vc, vd, ve, vf, f)); } @@ -681,11 +680,11 @@ public Option accumulate(final Semigroup s, final Validation< * @return A succeeding validation if all validations succeeded, or a failing validation with errors accumulated if * one or more failed. */ - public Validation accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf, final Validation vg, - final F8 f) { + public final Validation accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf, final Validation vg, + final F8 f) { return vg.accumapply(s, accumulate(s, va, vb, vc, vd, ve, vf, curry(f))); } @@ -703,10 +702,10 @@ public Option accumulate(final Semigroup s, final Validation< * @return A Some if one or more validations failed (accumulated with the semigroup), otherwise, * None. */ - public Option accumulate(final Semigroup s, final Validation va, - final Validation vb, final Validation vc, - final Validation vd, final Validation ve, - final Validation vf, final Validation vg) { + public final Option accumulate(final Semigroup s, final Validation va, + final Validation vb, final Validation vc, + final Validation vd, final Validation ve, + final Validation vf, final Validation vg) { return accumulate(s, va, vb, vc, vd, ve, vf, vg, (t, a, b, c, d, e1, f, g) -> unit()).f().toOption(); } @@ -715,97 +714,91 @@ public Option accumulate(final Semigroup s, final Validation< * * @return A iterator for this validation. */ - public Iterator iterator() { + public final Iterator iterator() { return toEither().right().iterator(); } - public Validation, T> accumulate() { + public final Validation, T> accumulate() { if (isFail()) { - return Validation.fail(List.single(fail())); + return fail(List.single(fail())); } else { - return Validation.success(success()); + return success(success()); } } - public Validation, B> accumulate(F f) { + public final Validation, B> accumulate(F f) { if (isFail()) { - return Validation.fail(List.single(fail())); + return fail(List.single(fail())); } else { - return Validation.success(f.f(success())); + return success(f.f(success())); } } - public 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 Validation.fail(list); - } else { - return Validation.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())); } + } - public Validation, D> accumulate(Validation v2, Validation v3, F3 f) { + public final Validation, D> accumulate(Validation v2, Validation v3, F3 f) { List list = fails(list(this, v2, v3)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success())); + return success(f.f(success(), v2.success(), v3.success())); } } - public Validation, $E> accumulate(Validation v2, Validation v3, Validation v4, F4 f) { + public final Validation, $E> accumulate(Validation v2, Validation v3, Validation v4, F4 f) { List list = fails(list(this, v2, v3, v4)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success(), v4.success())); + return success(f.f(success(), v2.success(), v3.success(), v4.success())); } } - public Validation, $F> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, F5 f) { + public final Validation, $F> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, F5 f) { List list = fails(list(this, v2, v3, v4, v5)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success())); + return success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success())); } } - public Validation, G> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, F6 f) { - List list = fails(list(this, v2, v3, v4, v5)); + 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, v6)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success())); + return success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success())); } } - public 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)); + 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, v6, v7)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success(), v7.success())); + return success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success(), v7.success())); } } - public 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)); + 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, v6, v7, v8)); if (!list.isEmpty()) { - return Validation.fail(list); + return fail(list); } else { - return Validation.success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success(), v7.success(), v8.success())); + return success(f.f(success(), v2.success(), v3.success(), v4.success(), v5.success(), v6.success(), v7.success(), v8.success())); } } @@ -816,53 +809,337 @@ public Validation, D> accumulate(Validation v2, Validati * single failure using a semigroup. */ public static Validation, List> sequenceNonCumulative(List> list) { - if (list.exists(v -> v.isFail())) { + if (list.exists(Validation::isFail)) { F2, Validation, List> f = (acc, v) -> acc.cons(v.fail()); - return Validation.fail(list.filter(v -> v.isFail()).foldLeft(f, List.nil()).reverse()); + return fail(list.filter(Validation::isFail).foldLeft(f, List.nil()).reverse()); } else { F2, Validation, List> f = (acc, v) -> acc.cons(v.success()); - return Validation.success(list.filter(v -> v.isSuccess()).foldLeft(f, List.nil()).reverse()); + return success(list.filter(Validation::isSuccess).foldLeft(f, List.nil()).reverse()); } } - public 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 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 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 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 P1> traverseP1(F> f){ - return isSuccess() ? - f.f(success()).map(Validation::success) : - P.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()); + } - public static List fails(List> list) { - return list.filter(v -> v.isFail()).map(v -> v.fail()); - } + /** + * 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 successes(List> list) { - return list.filter(v -> v.isSuccess()).map(v -> v.success()); - } + + /** + * 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()); + } + + /** + * 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. @@ -969,7 +1246,7 @@ public Validation map(final F f) { * @return A new validation value after binding. */ public Validation bind(final F> f) { - return v.isFail() ? f.f(v.fail()) : Validation.success(v.success()); + return v.isFail() ? f.f(v.fail()) : Validation.success(v.success()); } /** @@ -992,7 +1269,7 @@ public Validation sequence(final Validation v) { * failing value, otherwise, returns a fail in Some. */ public Option> filter(final F f) { - return v.toEither().left().filter(f).map(Validation.validation()); + return v.toEither().left().filter(f).map(Validation.validation()); } /** @@ -1002,7 +1279,7 @@ public Option> filter(final F f) { * @return The result of function application in validation. */ public Validation apply(final Validation, T> v) { - return v.f().bind(f -> map(f)); + return v.f().bind(this::map); } /** @@ -1080,11 +1357,11 @@ public Iterator iterator() { * * @return A validation with its failing value in a non-empty list if there is one. */ - @SuppressWarnings({"unchecked"}) - public Validation, T> nel() { + @SuppressWarnings("unchecked") + public final Validation, T> nel() { return isSuccess() ? - Validation., T>success(success()) : - Validation., T>fail(NonEmptyList.nel(fail())); + Validation.success(success()) : + Validation.fail(NonEmptyList.nel(fail())); } /** @@ -1103,7 +1380,7 @@ public static Validation validation(final Either e) { * @return A function that constructs a validation with an either. */ public static F, Validation> validation() { - return e1 -> validation(e1); + return Validation::validation; } /** @@ -1112,7 +1389,7 @@ public static F, Validation> validation() { * @return A function that constructs an either with a validation. */ public static F, Either> either() { - return v -> v.toEither(); + return Validation::toEither; } /** @@ -1122,7 +1399,7 @@ public static F, Either> either() { * @return A succeeding validation containing the given value. */ public static Validation success(final T t) { - return validation(Either.right(t)); + return validation(Either.right(t)); } /** @@ -1132,7 +1409,7 @@ public static Validation success(final T t) { * @return A failing validation containing the given value. */ public static Validation fail(final E e) { - return validation(Either.left(e)); + return validation(Either.left(e)); } /** @@ -1155,7 +1432,7 @@ public static Validation, T> failNEL(final E e) { * @return A validation based on a boolean condition. */ public static Validation condition(final boolean c, final E e, final T t) { - return c ? Validation.success(t) : Validation.fail(e); + return c ? Validation.success(t) : Validation.fail(e); } /** @@ -1175,7 +1452,7 @@ public static Validation parseByte(final String s) /** * A function that parses a string into a byte. */ - public static final F> parseByte = s -> parseByte(s); + public static final F> parseByte = Validation::parseByte; /** * Parses the given string into a double. @@ -1194,7 +1471,7 @@ public static Validation parseDouble(final String /** * A function that parses a string into a double. */ - public static final F> parseDouble = s -> parseDouble(s); + public static final F> parseDouble = Validation::parseDouble; /** * Parses the given string into a float. @@ -1213,7 +1490,7 @@ public static Validation parseFloat(final String s /** * A function that parses a string into a float. */ - public static final F> parseFloat = s -> parseFloat(s); + public static final F> parseFloat = Validation::parseFloat; /** * Parses the given string into a integer. @@ -1232,7 +1509,7 @@ public static Validation parseInt(final String s /** * A function that parses a string into an integer. */ - public static final F> parseInt = s -> parseInt(s); + public static final F> parseInt = Validation::parseInt; /** * Parses the given string into a long. @@ -1251,7 +1528,7 @@ public static Validation parseLong(final String s) /** * A function that parses a string into a long. */ - public static final F> parseLong = s -> parseLong(s); + public static final F> parseLong = Validation::parseLong; /** * Parses the given string into a short. @@ -1268,22 +1545,22 @@ 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 = s -> parseShort(s); + public static final F> parseShort = Validation::parseShort; /** * Partitions the list into the list of fails and the list of successes */ public static P2, List> partition(List> list) { - return P.p( - list.filter(v -> v.isFail()).map(v -> v.fail()), - list.filter(v -> v.isSuccess()).map(v -> v.success()) + return p( + list.filter(Validation::isFail).map(v -> v.fail()), + list.filter(Validation::isSuccess).map(v -> v.success()) ); } @Override - public String toString() { + public final String toString() { return Show.validationShow(Show.anyShow(), Show.anyShow()).showS(this); } diff --git a/core/src/main/java/fj/data/Writer.java b/core/src/main/java/fj/data/Writer.java index 96e449e3..ba2cded5 100644 --- a/core/src/main/java/fj/data/Writer.java +++ b/core/src/main/java/fj/data/Writer.java @@ -2,14 +2,11 @@ import fj.*; -/** - * Created by MarkPerry on 7/07/2014. - */ -public class Writer { +public final class Writer { - private A val; - private W logValue; - private Monoid monoid; + private final A val; + private final W logValue; + private final Monoid monoid; private Writer(A a, W w, Monoid m) { val = a; @@ -34,11 +31,11 @@ public Monoid monoid() { } public static Writer unit(A a, W w, Monoid m) { - return new Writer(a, w, m); + return new Writer<>(a, w, m); } public static Writer unit(A a, Monoid m) { - return new Writer(a, m.zero(), m); + return new Writer<>(a, m.zero(), m); } public Writer tell(W w) { @@ -59,7 +56,7 @@ public static Writer unit(B b) { } public static F> stringLogger() { - return a -> Writer.unit(a, Monoid.stringMonoid); + return a -> unit(a, Monoid.stringMonoid); } } diff --git a/core/src/main/java/fj/data/Zipper.java b/core/src/main/java/fj/data/Zipper.java index a4377507..4b96377f 100644 --- a/core/src/main/java/fj/data/Zipper.java +++ b/core/src/main/java/fj/data/Zipper.java @@ -1,586 +1,585 @@ -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.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 (l, a, r) -> zipper(l, a, r); - } - - /** - * 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 a -> a.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_()); - } - - /** - * 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 as -> as.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 as -> as.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., Zipper>p2())), this); - final Stream> right = Stream.unfold( - p -> p.next().map(join(P., Zipper>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/Deep.java b/core/src/main/java/fj/data/fingertrees/Deep.java index e42bde4d..f9d6eb83 100644 --- a/core/src/main/java/fj/data/fingertrees/Deep.java +++ b/core/src/main/java/fj/data/fingertrees/Deep.java @@ -2,6 +2,7 @@ import fj.*; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; @@ -9,6 +10,7 @@ import static fj.Function.constant; import static fj.data.List.list; import static fj.Function.flip; +import static fj.data.Stream.nil; /** * A finger tree with 1-4-digits on the left and right, and a finger tree of 2-3-nodes in the middle. @@ -57,24 +59,24 @@ public Digit suffix() { } @Override public B foldRight(final F> aff, final B z) { - return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.foldRight(aff, z))); + return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.foldRight(aff, z))); } @Override public A reduceRight(final F> aff) { - return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.reduceRight(aff))); + return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.reduceRight(aff))); } @Override public B foldLeft(final F> bff, final B z) { - return suffix.foldLeft(bff, middle.foldLeft(Node.foldLeft_(bff), prefix.foldLeft(bff, z))); + return suffix.foldLeft(bff, middle.foldLeft(Node.foldLeft_(bff), prefix.foldLeft(bff, z))); } @Override public A reduceLeft(final F> aff) { - return suffix.foldLeft(aff, middle.foldLeft(Node.foldLeft_(aff), prefix.reduceLeft(aff))); + return suffix.foldLeft(aff, middle.foldLeft(Node.foldLeft_(aff), prefix.reduceLeft(aff))); } @Override public FingerTree map(final F abf, final Measured m) { - return new Deep(m, v, prefix.map(abf, m), middle.map(Node.liftM(abf, m), m.nodeMeasured()), - suffix.map(abf, m)); + return new Deep<>(m, v, prefix.map(abf, m), middle.map(Node.liftM(abf, m), m.nodeMeasured()), + suffix.map(abf, m)); } /** @@ -134,14 +136,14 @@ public FingerTree snoc(final A a) { four -> four.values()._4()); } - private static final FingerTree deepL(final Measured measured, final Option> lOpt, final FingerTree> m, final Digit r) { + private static FingerTree deepL(final Measured measured, final Option> lOpt, final FingerTree> m, final Digit r) { return lOpt.option( P.lazy(() -> m.isEmpty() ? r.toTree() : mkTree(measured).deep(m.head().toDigit(), m.tail(), r)), (F, FingerTree>) l -> mkTree(measured).deep(l, m, r) ); } - private static final FingerTree deepR(final Measured measured, final Option> rOpt, final FingerTree> m, final Digit l) { + private static FingerTree deepR(final Measured measured, final Option> rOpt, final FingerTree> m, final Digit l) { return rOpt.option( P.lazy(() -> m.isEmpty() ? l.toTree() : mkTree(measured).deep(l, m.init(), m.last().toDigit())), (F, FingerTree>) r -> mkTree(measured).deep(l, m, r) @@ -208,23 +210,21 @@ private static FingerTree> addDigits0( final MakeTree mk = mkTree(m); return s1.match( - one1 -> { - return p2.match( - one2 -> append1(m, m1, mk.node2(one1.value(), one2.value()), m2), - two2 -> { - final V2 vs = two2.values(); - return append1(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), m2); - }, - three -> { - final V3 vs = three.values(); - return append2(m, m1, mk.node2(one1.value(), vs._1()), mk.node2(vs._2(), vs._3()), m2); - }, - four -> { - final V4 vs = four.values(); - return append2(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), mk.node2(vs._3(), vs._4()), m2); - } - ); - }, + one1 -> p2.match( + one2 -> append1(m, m1, mk.node2(one1.value(), one2.value()), m2), + two2 -> { + final V2 vs = two2.values(); + return append1(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), m2); + }, + three -> { + final V3 vs = three.values(); + return append2(m, m1, mk.node2(one1.value(), vs._1()), mk.node2(vs._2(), vs._3()), m2); + }, + four -> { + final V4 vs = four.values(); + return append2(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), mk.node2(vs._3(), vs._4()), m2); + } + ), two1 -> { final V2 v1 = two1.values(); return p2.match( @@ -281,34 +281,12 @@ private static FingerTree> addDigits0( private static FingerTree> append1(final Measured m, final FingerTree> xs, final Node a, final FingerTree> ys) { - return xs.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return ys.cons(a); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return ys.cons(a).cons(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep1) { - return ys.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return xs.snoc(a); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return xs.snoc(a).snoc(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep2) { - final Measured> nm = m.nodeMeasured(); - return new Deep>(nm, m.sum(m.sum(deep1.v, nm.measure(a)), deep2.v), deep1.prefix, - addDigits1(nm, deep1.middle, deep1.suffix, a, deep2.prefix, deep2.middle), - deep2.suffix); - } - }); - } - }); + return xs.match(empty -> ys.cons(a), single -> ys.cons(a).cons(single.value()), deep1 -> ys.match(empty -> xs.snoc(a), single -> xs.snoc(a).snoc(single.value()), deep2 -> { + final Measured> nm = m.nodeMeasured(); + return new Deep<>(nm, m.sum(m.sum(deep1.v, nm.measure(a)), deep2.v), deep1.prefix, + addDigits1(nm, deep1.middle, deep1.suffix, a, deep2.prefix, deep2.middle), + deep2.suffix); + })); } private static FingerTree>> addDigits1(final Measured> m, @@ -317,136 +295,54 @@ private static FingerTree>> addDigits1(final Measur final Digit> y, final FingerTree>> m2) { final MakeTree> mk = mkTree(m); - return x.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one1) { - return y.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one2) { - return append1(m, m1, mk.node3(one1.value(), n, one2.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append2(m, m1, mk.node2(one1.value(), n), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two1) { - final V2> v1 = two1.values(); - return y.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node2(v1), mk.node2(n, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node3(three.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), - m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v1 = three.values(); - return y.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node3(v1), mk.node2(n, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v2 = two.values(); - return append2(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(v1), mk.node2(n, v2._1()), mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v1 = four.values(); - return y.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n), mk.node2(two.values()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()), - mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()), - mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } + return x.match(one1 -> y.match(one2 -> append1(m, m1, mk.node3(one1.value(), n, one2.value()), m2), two -> append2(m, m1, mk.node2(one1.value(), n), mk.node2(two.values()), m2), three -> { + final V3> v2 = three.values(); + return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, four -> { + final V4> v2 = four.values(); + return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); + }), two1 -> { + final V2> v1 = two1.values(); + return y.match(one -> append2(m, m1, mk.node2(v1), mk.node2(n, one.value()), m2), two -> append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(two.values()), m2), three -> append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node3(three.values()), m2), four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), + m2); + }); + }, three -> { + final V3> v1 = three.values(); + return y.match(one -> append2(m, m1, mk.node3(v1), mk.node2(n, one.value()), m2), two -> { + final V2> v2 = two.values(); + return append2(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), m2); + }, three1 -> { + final V3> v2 = three1.values(); + return append3(m, m1, mk.node3(v1), mk.node2(n, v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); + }); + }, four -> { + final V4> v1 = four.values(); + return y.match(one -> append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, one.value()), m2), two -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n), mk.node2(two.values()), + m2), three -> { + final V3> v2 = three.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()), + mk.node2(v2._2(), v2._3()), m2); + }, four1 -> { + final V4> v2 = four1.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()), + mk.node3(v2._2(), v2._3(), v2._4()), m2); + }); }); } private static FingerTree> append2(final Measured m, final FingerTree> t1, final Node n1, final Node n2, final FingerTree> t2) { - return t1.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return t2.cons(n2).cons(n1); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return t2.cons(n2).cons(n1).cons(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep) { - return t2.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return deep.snoc(n1).snoc(n2); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return deep.snoc(n1).snoc(n2).snoc(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep2) { - return new Deep>(m.nodeMeasured(), - m.sum(m.sum(m.sum(deep.measure(), n1.measure()), n2.measure()), - deep2.measure()), deep.prefix, - addDigits2(m.nodeMeasured(), deep.middle, deep.suffix, n1, n2, deep2.prefix, - deep2.middle), deep2.suffix); - } - }); - } - }); + return t1.match(empty -> t2.cons(n2).cons(n1), single -> t2.cons(n2).cons(n1).cons(single.value()), deep -> t2.match(empty -> deep.snoc(n1).snoc(n2), single -> deep.snoc(n1).snoc(n2).snoc(single.value()), deep2 -> new Deep<>(m.nodeMeasured(), + m.sum(m.sum(m.sum(deep.measure(), n1.measure()), n2.measure()), + deep2.measure()), deep.prefix, + addDigits2(m.nodeMeasured(), deep.middle, deep.suffix, n1, n2, deep2.prefix, + deep2.middle), deep2.suffix))); } private static FingerTree>> addDigits2(final Measured> m, @@ -456,103 +352,43 @@ private static FingerTree>> addDigits2(final Measur final Digit> prefix, final FingerTree>> m2) { final MakeTree> mk = mkTree(m); - return suffix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one2) { - return append2(m, m1, mk.node2(one.value(), n1), mk.node2(n2, one2.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(three.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), - m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v1 = two.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two2) { - final V2> v2 = two2.values(); - return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, v2._1()), mk.node2(v2._2(), v2._3()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v1 = three.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node3(v1), mk.node3(n1, n2, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(v1), mk.node2(n1, n2), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three2) { - final V3> v2 = three2.values(); - return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v1 = four.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n1), mk.node2(n2, one.value()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(three.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four2) { - final V4> v2 = four2.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); - } - }); - } + return suffix.match(one -> prefix.match(one2 -> append2(m, m1, mk.node2(one.value(), n1), mk.node2(n2, one2.value()), m2), two -> append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(two.values()), m2), three -> append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(three.values()), m2), four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), + m2); + }), two -> { + final V2> v1 = two.values(); + return prefix.match(one -> append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, one.value()), m2), two2 -> { + final V2> v2 = two2.values(); + return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()), m2); + }, three -> { + final V3> v2 = three.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, v2._1()), mk.node2(v2._2(), v2._3()), + m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()), + mk.node2(v2._3(), v2._4()), m2); + }); + }, three -> { + final V3> v1 = three.values(); + return prefix.match(one -> append2(m, m1, mk.node3(v1), mk.node3(n1, n2, one.value()), m2), two -> append3(m, m1, mk.node3(v1), mk.node2(n1, n2), mk.node2(two.values()), m2), three2 -> { + final V3> v2 = three2.values(); + return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); + }); + }, four -> { + final V4> v1 = four.values(); + return prefix.match(one -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n1), mk.node2(n2, one.value()), + m2), two -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node2(two.values()), m2), three -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(three.values()), m2), four2 -> { + final V4> v2 = four2.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); + }); }); } @@ -561,34 +397,10 @@ private static FingerTree> append3(final Measured m, final Node n1, final Node n2, final Node n3, final FingerTree> t2) { final Measured> nm = m.nodeMeasured(); - return t1.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return t2.cons(n3).cons(n2).cons(n1); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return t2.cons(n3).cons(n2).cons(n1).cons(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep) { - return t2.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return deep.snoc(n1).snoc(n2).snoc(n3); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return deep.snoc(n1).snoc(n2).snoc(n3).snoc(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep2) { - return new Deep>(nm, nm.monoid().sumLeft( - list(deep.v, n1.measure(), n2.measure(), n3.measure(), deep2.v)), deep.prefix, - addDigits3(nm, deep.middle, deep.suffix, n1, n2, n3, deep2.prefix, - deep2.middle), deep2.suffix); - } - }); - } - }); + return t1.match(empty -> t2.cons(n3).cons(n2).cons(n1), single -> t2.cons(n3).cons(n2).cons(n1).cons(single.value()), deep -> t2.match(empty -> deep.snoc(n1).snoc(n2).snoc(n3), single -> deep.snoc(n1).snoc(n2).snoc(n3).snoc(single.value()), deep2 -> new Deep<>(nm, nm.monoid().sumLeft( + list(deep.v, n1.measure(), n2.measure(), n3.measure(), deep2.v)), deep.prefix, + addDigits3(nm, deep.middle, deep.suffix, n1, n2, n3, deep2.prefix, + deep2.middle), deep2.suffix))); } private static FingerTree>> addDigits3(final Measured> m, @@ -599,105 +411,47 @@ private static FingerTree>> addDigits3(final Measur final Digit> prefix, final FingerTree>> m2) { final MakeTree> mk = mkTree(m); - return suffix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one2) { - return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, one2.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v2 = two.values(); - return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, v2._1()), mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v1 = two.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()), mk.node2(v2._2(), v2._3()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()), - mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(three.values()), mk.node2(n1, n2), mk.node2(n3, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three2) { - return append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node3(three2.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append4(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v1 = four.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node2(n3, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v2 = two.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(n3, v2._1(), v2._2()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), mk.node2(n3, v2._1()), - mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four2) { - final V4> v2 = four2.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(n3, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); - } - }); - } + return suffix.match(one -> prefix.match(one2 -> append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, one2.value()), m2), two -> { + final V2> v2 = two.values(); + return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()), m2); + }, three -> { + final V3> v2 = three.values(); + return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()), + mk.node2(v2._3(), v2._4()), m2); + }), two -> { + final V2> v1 = two.values(); + return prefix.match(one -> append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, one.value()), m2), two1 -> append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(two1.values()), m2), three -> { + final V3> v2 = three.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()), mk.node2(v2._2(), v2._3()), + m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()), + mk.node3(v2._2(), v2._3(), v2._4()), m2); + }); + }, three -> prefix.match(one -> append3(m, m1, mk.node3(three.values()), mk.node2(n1, n2), mk.node2(n3, one.value()), m2), two -> append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(two.values()), m2), three2 -> append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node3(three2.values()), m2), four -> { + final V4> v2 = four.values(); + return append4(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(v2._1(), v2._2()), + mk.node2(v2._3(), v2._4()), m2); + }), four -> { + final V4> v1 = four.values(); + return prefix.match(one -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node2(n3, one.value()), m2), two -> { + final V2> v2 = two.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(n3, v2._1(), v2._2()), m2); + }, three -> { + final V3> v2 = three.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), mk.node2(n3, v2._1()), + mk.node2(v2._2(), v2._3()), m2); + }, four2 -> { + final V4> v2 = four2.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(n3, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); + }); }); } @@ -710,34 +464,10 @@ private static FingerTree> append4(final Measured m, final Node n4, final FingerTree> t2) { final Measured> nm = m.nodeMeasured(); - return t1.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return t2.cons(n4).cons(n3).cons(n2).cons(n1); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return t2.cons(n4).cons(n3).cons(n2).cons(n1).cons(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep) { - return t2.match(new F>, FingerTree>>() { - public FingerTree> f(final Empty> empty) { - return t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Single> single) { - return t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4).snoc(single.value()); - } - }, new F>, FingerTree>>() { - public FingerTree> f(final Deep> deep2) { - return new Deep>(nm, m.monoid().sumLeft( - list(deep.v, n1.measure(), n2.measure(), n3.measure(), n4.measure(), deep2.v)), deep.prefix, - addDigits4(nm, deep.middle, deep.suffix, n1, n2, n3, n4, deep2.prefix, - deep2.middle), deep2.suffix); - } - }); - } - }); + return t1.match(empty -> t2.cons(n4).cons(n3).cons(n2).cons(n1), single -> t2.cons(n4).cons(n3).cons(n2).cons(n1).cons(single.value()), deep -> t2.match(empty -> t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4), single -> t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4).snoc(single.value()), deep2 -> new Deep<>(nm, m.monoid().sumLeft( + list(deep.v, n1.measure(), n2.measure(), n3.measure(), n4.measure(), deep2.v)), deep.prefix, + addDigits4(nm, deep.middle, deep.suffix, n1, n2, n3, n4, deep2.prefix, + deep2.middle), deep2.suffix))); } private static FingerTree>> addDigits4(final Measured> m, @@ -748,106 +478,63 @@ private static FingerTree>> addDigits4(final Measur final Digit> prefix, final FingerTree>> m2) { final MakeTree> mk = mkTree(m); - return suffix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one2) { - return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, one2.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, n4), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()), - mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v1 = two.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(n4, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two2) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(two2.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node3(three.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v1 = three.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - final V2> v2 = two.values(); - return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, v2._1()), mk.node2(v2._2(), v2._3()), - m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v1 = four.values(); - return prefix.match(new F>, FingerTree>>>() { - public FingerTree>> f(final One> one) { - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(n3, n4, one.value()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Two> two) { - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node2(n3, n4), mk.node2(two.values()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Three> three) { - final V3> v2 = three.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()), m2); - } - }, new F>, FingerTree>>>() { - public FingerTree>> f(final Four> four) { - final V4> v2 = four.values(); - return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), - mk.node3(n3, n4, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } + return suffix.match(one -> prefix.match(one2 -> append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, one2.value()), m2), two -> append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, n4), mk.node2(two.values()), m2), three -> { + final V3> v2 = three.values(); + return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()), + m2); + }, four -> { + final V4> v2 = four.values(); + return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()), + mk.node3(v2._2(), v2._3(), v2._4()), m2); + }), two -> { + final V2> v1 = two.values(); + return prefix.match(one -> append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(n4, one.value()), m2), two2 -> append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(two2.values()), m2), three -> append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node3(three.values()), m2), four -> { + final V4> v2 = four.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(v2._1(), v2._2()), + mk.node2(v2._3(), v2._4()), m2); + }); + }, three -> { + final V3> v1 = three.values(); + return prefix.match(one -> append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, one.value()), m2), two -> { + final V2> v2 = two.values(); + return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()), m2); + }, three1 -> { + final V3> v2 = three1.values(); + return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, v2._1()), mk.node2(v2._2(), v2._3()), + m2); + }, four -> { + final V4> v2 = four.values(); + return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()), + mk.node2(v2._3(), v2._4()), m2); + }); + }, four -> { + final V4> v1 = four.values(); + return prefix.match(one -> append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(n3, n4, one.value()), m2), two -> append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node2(n3, n4), mk.node2(two.values()), m2), three -> { + final V3> v2 = three.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, four1 -> { + final V4> v2 = four1.values(); + return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), + mk.node3(n3, n4, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); + }); }); } + + public String toString() { + return Show.fingerTreeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return prefix().toStream().append(() -> + middle().match( + e -> Stream.nil(), + s -> s.value().toStream(), + d -> d.toStream().bind(p -> p.toStream()) + ) + ).append(() -> suffix.toStream()); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/Digit.java b/core/src/main/java/fj/data/fingertrees/Digit.java index debf2635..fe76d1df 100644 --- a/core/src/main/java/fj/data/fingertrees/Digit.java +++ b/core/src/main/java/fj/data/fingertrees/Digit.java @@ -2,6 +2,7 @@ import fj.*; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; @@ -38,7 +39,7 @@ public abstract class Digit { */ public final A reduceRight(final F> f) { return match( - one -> one.value(), + One::value, two -> { final V2 v = two.values(); return f.f(v._1()).f(v._2()); @@ -62,7 +63,7 @@ public final A reduceRight(final F> f) { */ public final A reduceLeft(final F> f) { return match( - one -> one.value(), + One::value, two -> { final V2 v = two.values(); return f.f(v._1()).f(v._2()); @@ -88,10 +89,10 @@ public final A reduceLeft(final F> f) { */ public final Digit map(final F f, final Measured m) { return match( - one -> new One(m, f.f(one.value())), - two -> new Two(m, two.values().map(f)), - three -> new Three(m, three.values().map(f)), - four -> new Four(m, four.values().map(f)) + one -> new One<>(m, f.f(one.value())), + two -> new Two<>(m, two.values().map(f)), + three -> new Three<>(m, three.values().map(f)), + four -> new Four<>(m, four.values().map(f)) ); } @@ -132,27 +133,27 @@ public final FingerTree toTree() { final MakeTree mk = mkTree(m); return match( one -> mk.single(one.value()), - two -> mk.deep(mk.one(two.values()._1()), new Empty>(m.nodeMeasured()), mk.one(two.values()._2())), - three -> mk.deep(mk.two(three.values()._1(), three.values()._2()), new Empty>(m.nodeMeasured()), mk.one(three.values()._3())), - four -> mk.deep(mk.two(four.values()._1(), four.values()._2()), new Empty>(m.nodeMeasured()), mk.two(four.values()._3(), four.values()._4())) + two -> mk.deep(mk.one(two.values()._1()), new Empty<>(m.nodeMeasured()), mk.one(two.values()._2())), + three -> mk.deep(mk.two(three.values()._1(), three.values()._2()), new Empty<>(m.nodeMeasured()), mk.one(three.values()._3())), + four -> mk.deep(mk.two(four.values()._1(), four.values()._2()), new Empty<>(m.nodeMeasured()), mk.two(four.values()._3(), four.values()._4())) ); } - Option> tail() { + final Option> tail() { return match( - one -> Option.> none(), - two -> Option.> some(mkTree(m).one(two.values()._2())), - three -> Option.> some(mkTree(m).two(three.values()._2(), three.values()._3())), - four -> Option.> some(mkTree(m).three(four.values()._2(), four.values()._3(), four.values()._4())) + one -> Option.none(), + two -> Option.some(mkTree(m).one(two.values()._2())), + three -> Option.some(mkTree(m).two(three.values()._2(), three.values()._3())), + four -> Option.some(mkTree(m).three(four.values()._2(), four.values()._3(), four.values()._4())) ); } - Option> init() { + final Option> init() { return match( - one -> Option.> none(), - two -> Option.> some(mkTree(m).one(two.values()._1())), - three -> Option.> some(mkTree(m).two(three.values()._1(), three.values()._2())), - four -> Option.> some(mkTree(m).three(four.values()._1(), four.values()._2(), four.values()._3())) + one -> Option.none(), + two -> Option.some(mkTree(m).one(two.values()._1())), + three -> Option.some(mkTree(m).two(three.values()._1(), three.values()._2())), + four -> Option.some(mkTree(m).three(four.values()._1(), four.values()._2(), four.values()._3())) ); } @@ -162,4 +163,11 @@ Option> init() { public abstract int length(); + public String toString() { + return Show.digitShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public abstract Stream toStream(); + + } diff --git a/core/src/main/java/fj/data/fingertrees/Empty.java b/core/src/main/java/fj/data/fingertrees/Empty.java index 17ea5927..275b1cec 100644 --- a/core/src/main/java/fj/data/fingertrees/Empty.java +++ b/core/src/main/java/fj/data/fingertrees/Empty.java @@ -1,9 +1,10 @@ package fj.data.fingertrees; import fj.F; -import fj.P; import fj.P2; import fj.P3; +import fj.Show; +import fj.data.Stream; import static fj.Bottom.error; @@ -16,7 +17,7 @@ public final class Empty extends FingerTree { } @Override public FingerTree cons(final A a) { - return new Single(measured(), a); + return new Single<>(measured(), a); } @Override public FingerTree snoc(final A a) { @@ -59,7 +60,7 @@ public A reduceRight(final F> aff) { } @Override public FingerTree map(final F abf, final Measured m) { - return new Empty(m); + return new Empty<>(m); } /** @@ -82,4 +83,13 @@ public V measure() { @Override P3, A, FingerTree> split1(final F predicate, final V acc) { throw error("Splitting an empty tree"); } + + public String toString() { + return Show.fingerTreeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return Stream.nil(); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/FingerTree.java b/core/src/main/java/fj/data/fingertrees/FingerTree.java index 9f86db45..ab0effb0 100644 --- a/core/src/main/java/fj/data/fingertrees/FingerTree.java +++ b/core/src/main/java/fj/data/fingertrees/FingerTree.java @@ -1,231 +1,268 @@ -package fj.data.fingertrees; - -import fj.*; -import fj.data.Option; -import fj.data.Seq; - -import static fj.Monoid.intAdditionMonoid; - -/** - * 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 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 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 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; - } - - 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 Option headOption() { - return isEmpty() ? Option.none() : Option.some(head()); - } - - /** - * 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 FingerTree.mkTree(FingerTree.measured(intAdditionMonoid, Function.constant(1))).empty(); - } - -} +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/main/java/fj/data/fingertrees/Four.java b/core/src/main/java/fj/data/fingertrees/Four.java index c66ddeca..31a40d72 100644 --- a/core/src/main/java/fj/data/fingertrees/Four.java +++ b/core/src/main/java/fj/data/fingertrees/Four.java @@ -3,7 +3,9 @@ import fj.P; import fj.P2; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V4; import fj.F; @@ -88,4 +90,13 @@ public V4 values() { public int length() { return 4; } + + public String toString() { + return Show.digitShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return values().toStream(); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/MakeTree.java b/core/src/main/java/fj/data/fingertrees/MakeTree.java index b435d955..b2a3da84 100644 --- a/core/src/main/java/fj/data/fingertrees/MakeTree.java +++ b/core/src/main/java/fj/data/fingertrees/MakeTree.java @@ -14,7 +14,7 @@ public final class MakeTree { MakeTree(final Measured m) { this.m = m; - this.empty = new Empty(m); + this.empty = new Empty<>(m); } // Tree constructors @@ -35,7 +35,7 @@ public FingerTree empty() { * @return A tree with the given value as the single element. */ public FingerTree single(final A a) { - return new Single(m, a); + return new Single<>(m, a); } /** @@ -63,7 +63,7 @@ public FingerTree deep(final Digit prefix, final FingerTree deep(final V v, final Digit prefix, final FingerTree> middle, final Digit suffix) { - return new Deep(m, v, prefix, middle, suffix); + return new Deep<>(m, v, prefix, middle, suffix); } // Digit constructors @@ -75,7 +75,7 @@ public FingerTree deep(final V v, final Digit prefix, final FingerTr * @return A digit of the given element. */ public One one(final A a) { - return new One(m, a); + return new One<>(m, a); } /** @@ -86,7 +86,7 @@ public One one(final A a) { * @return A digit of the given elements. */ public Two two(final A a, final A b) { - return new Two(m, v(a, b)); + return new Two<>(m, v(a, b)); } /** @@ -98,7 +98,7 @@ public Two two(final A a, final A b) { * @return A digit of the given elements. */ public Three three(final A a, final A b, final A c) { - return new Three(m, v(a, b, c)); + return new Three<>(m, v(a, b, c)); } /** @@ -111,7 +111,7 @@ public Three three(final A a, final A b, final A c) { * @return A digit of the given elements. */ public Four four(final A a, final A b, final A c, final A d) { - return new Four(m, v(a, b, c, d)); + return new Four<>(m, v(a, b, c, d)); } // Node constructors @@ -124,7 +124,7 @@ public Four four(final A a, final A b, final A c, final A d) { * @return A new binary tree node. */ public Node2 node2(final A a, final A b) { - return new Node2(m, v(a, b)); + return new Node2<>(m, v(a, b)); } /** @@ -136,7 +136,7 @@ public Node2 node2(final A a, final A b) { * @return A new trinary tree node. */ public Node3 node3(final A a, final A b, final A c) { - return new Node3(m, v(a, b, c)); + return new Node3<>(m, v(a, b, c)); } /** @@ -146,7 +146,7 @@ public Node3 node3(final A a, final A b, final A c) { * @return A new binary tree node. */ public Node2 node2(final V2 v) { - return new Node2(m, v); + return new Node2<>(m, v); } /** @@ -156,7 +156,7 @@ public Node2 node2(final V2 v) { * @return A new trinary tree node. */ public Node3 node3(final V3 v) { - return new Node3(m, v); + return new Node3<>(m, v); } } diff --git a/core/src/main/java/fj/data/fingertrees/Measured.java b/core/src/main/java/fj/data/fingertrees/Measured.java index 777eb369..28c94abe 100644 --- a/core/src/main/java/fj/data/fingertrees/Measured.java +++ b/core/src/main/java/fj/data/fingertrees/Measured.java @@ -17,7 +17,7 @@ private Measured(final Monoid m, final F measure) { } public static Measured measured(final Monoid m, final F measure) { - return new Measured(m, measure); + return new Measured<>(m, measure); } /** @@ -74,7 +74,7 @@ public V zero() { * @return A measured instance for nodes. */ public Measured> nodeMeasured() { - return new Measured>(m, node -> node.measure()); + return new Measured<>(m, Node::measure); } /** @@ -83,7 +83,7 @@ public Measured> nodeMeasured() { * @return A measured instance for digits. */ public Measured> digitMeasured() { - return new Measured>(m, d -> d.measure()); + return new Measured<>(m, Digit::measure); } } diff --git a/core/src/main/java/fj/data/fingertrees/Node.java b/core/src/main/java/fj/data/fingertrees/Node.java index 3e451766..ff98b337 100644 --- a/core/src/main/java/fj/data/fingertrees/Node.java +++ b/core/src/main/java/fj/data/fingertrees/Node.java @@ -1,10 +1,11 @@ package fj.data.fingertrees; import fj.F; -import fj.F2; +import fj.P; import fj.P2; import fj.P3; import fj.data.Option; +import fj.data.Stream; import static fj.Function.curry; @@ -29,8 +30,8 @@ public static F, B>> foldRight_(final F> af public final Node map(final F f, final Measured m) { return match( - node2 -> new Node2(m, node2.toVector().map(f)), - node3 -> new Node3(m, node3.toVector().map(f)) + node2 -> new Node2<>(m, node2.toVector().map(f)), + node3 -> new Node3<>(m, node3.toVector().map(f)) ); } @@ -49,7 +50,7 @@ public final V measure() { return measure; } - Measured measured() { + final Measured measured() { return m; } @@ -60,4 +61,7 @@ Measured measured() { public abstract B match(final F, B> n2, final F, B> n3); public abstract int length(); + + public abstract Stream toStream(); + } diff --git a/core/src/main/java/fj/data/fingertrees/Node2.java b/core/src/main/java/fj/data/fingertrees/Node2.java index 5fc979be..56e668e7 100644 --- a/core/src/main/java/fj/data/fingertrees/Node2.java +++ b/core/src/main/java/fj/data/fingertrees/Node2.java @@ -2,7 +2,9 @@ import fj.P; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V2; import fj.F; import fj.P2; @@ -31,7 +33,7 @@ public final class Node2 extends Node { } public Digit toDigit() { - return new Two(measured(), as); + return new Two<>(measured(), as); } P3>, A, Option>> split1(final F predicate, final V acc) { @@ -66,4 +68,13 @@ public int length() { public V2 toVector() { return as; } + + public String toString() { + return Show.nodeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return as.toStream(); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/Node3.java b/core/src/main/java/fj/data/fingertrees/Node3.java index 233fcda7..c803239f 100644 --- a/core/src/main/java/fj/data/fingertrees/Node3.java +++ b/core/src/main/java/fj/data/fingertrees/Node3.java @@ -2,7 +2,9 @@ import fj.P; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V3; import fj.F; import fj.P2; @@ -40,7 +42,7 @@ public int length() { } public Digit toDigit() { - return new Three(measured(), as); + return new Three<>(measured(), as); } P3>, A, Option>> split1(final F predicate, final V acc) { @@ -74,4 +76,13 @@ P3>, A, Option>> split1(final F predi public V3 toVector() { return as; } + + public String toString() { + return Show.nodeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return as.toStream(); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/One.java b/core/src/main/java/fj/data/fingertrees/One.java index d07cab60..f3711ace 100644 --- a/core/src/main/java/fj/data/fingertrees/One.java +++ b/core/src/main/java/fj/data/fingertrees/One.java @@ -4,7 +4,9 @@ import fj.P; import fj.P2; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import static fj.data.Option.none; @@ -54,4 +56,13 @@ public A value() { public int length() { return 1; } + + public String toString() { + return Show.digitShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return Stream.single(a); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/Single.java b/core/src/main/java/fj/data/fingertrees/Single.java index 410378f1..57ea0897 100644 --- a/core/src/main/java/fj/data/fingertrees/Single.java +++ b/core/src/main/java/fj/data/fingertrees/Single.java @@ -4,8 +4,11 @@ import fj.P; import fj.P2; import fj.P3; +import fj.Show; +import fj.data.Stream; import static fj.P.p; +import static fj.Show.anyShow; /** * A tree with a single element. @@ -37,7 +40,7 @@ public final class Single extends FingerTree { } @Override public FingerTree map(final F abf, final Measured m) { - return new Single(m, abf.f(a)); + return new Single<>(m, abf.f(a)); } /** @@ -59,12 +62,12 @@ public V measure() { @Override public FingerTree cons(final A b) { final MakeTree mk = mkTree(measured()); - return mk.deep(mk.one(b), new Empty>(measured().nodeMeasured()), mk.one(a)); + return mk.deep(mk.one(b), new Empty<>(measured().nodeMeasured()), mk.one(a)); } @Override public FingerTree snoc(final A b) { final MakeTree mk = mkTree(measured()); - return mk.deep(mk.one(a), new Empty>(measured().nodeMeasured()), mk.one(b)); + return mk.deep(mk.one(a), new Empty<>(measured().nodeMeasured()), mk.one(b)); } @Override public A head() { return a; } @@ -81,7 +84,7 @@ public V measure() { @Override P3, A, FingerTree> split1(final F predicate, final V acc) { final Empty empty = new Empty<>(measured()); - return P.p(empty, a, empty); + return p(empty, a, empty); } @Override public P2 lookup(final F o, final int i) { return p(i, a); } @@ -99,4 +102,13 @@ public int length() { public A value() { return a; } + + public String toString() { + return Show.fingerTreeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return Stream.single(a); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/Three.java b/core/src/main/java/fj/data/fingertrees/Three.java index da9f6a9e..2db896b3 100644 --- a/core/src/main/java/fj/data/fingertrees/Three.java +++ b/core/src/main/java/fj/data/fingertrees/Three.java @@ -3,7 +3,9 @@ import fj.P; import fj.P2; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V3; import fj.F; @@ -78,4 +80,12 @@ public V3 values() { public int length() { return 3; } + + public String toString() { + return Show.digitShow(Show.anyShow(), Show.anyShow()).showS(this); + } + public Stream toStream() { + return values().toStream(); + } + } diff --git a/core/src/main/java/fj/data/fingertrees/Two.java b/core/src/main/java/fj/data/fingertrees/Two.java index 3029fd9d..cb8a2162 100644 --- a/core/src/main/java/fj/data/fingertrees/Two.java +++ b/core/src/main/java/fj/data/fingertrees/Two.java @@ -3,7 +3,9 @@ import fj.P; import fj.P2; import fj.P3; +import fj.Show; import fj.data.Option; +import fj.data.Stream; import fj.data.vector.V2; import fj.F; @@ -70,4 +72,12 @@ public int length() { return 2; } + public String toString() { + return Show.digitShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + public Stream toStream() { + return values().toStream(); + } + } diff --git a/core/src/main/java/fj/data/hamt/BitSet.java b/core/src/main/java/fj/data/hamt/BitSet.java new file mode 100644 index 00000000..9efbab34 --- /dev/null +++ b/core/src/main/java/fj/data/hamt/BitSet.java @@ -0,0 +1,202 @@ +package fj.data.hamt; + +import fj.Equal; +import fj.F2; +import fj.Monoid; +import fj.Show; +import fj.data.List; +import fj.data.Stream; + +/** + * A sequence of bits representing a value. The most significant bit (the + * bit with the highest value) is the leftmost bit and has the highest index. + * 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. + */ +public final class BitSet { + + public static final int TRUE_BIT = 1; + public static final int FALSE_BIT = 0; + public static final BitSet EMPTY = new BitSet(FALSE_BIT); + + public static final long BASE_LONG = 1L; + public static final int MAX_BIT_SIZE = Long.SIZE; + public static final int MAX_BIT_INDEX = Long.SIZE - 1; + + private final long value; + + private BitSet(final long l) { + value = l; + } + + public static BitSet empty() { + return EMPTY; + } + + public static BitSet longBitSet(final long l) { + return new BitSet(l); + } + + public static BitSet listBitSet(final List list) { + final int n = list.length(); + if (n > MAX_BIT_SIZE) { + throw new IllegalArgumentException("Does not support lists greater than " + MAX_BIT_SIZE + " bits, actual size is " + n); + } + long result = 0; + for (Boolean b: list) { + result = (result << 1) | toInt(b); + } + return longBitSet(result); + } + + public static BitSet streamBitSet(final Stream s) { + return listBitSet(s.toList()); + } + + public static BitSet stringBitSet(final String s) { + return streamBitSet(Stream.fromString(s).map(BitSet::toBoolean)); + } + + public boolean isSet(final int index) { + return (value & (BASE_LONG << index)) != 0; + } + + public boolean isEmpty() { + return value == 0; + } + + public BitSet set(final int index) { + return longBitSet(value | (BASE_LONG << index)); + } + + public BitSet set(final int index, boolean b) { + return b ? set(index) : clear(index); + } + + public BitSet clear(final int index) { + return longBitSet(value & ~(BASE_LONG << index)); + } + + public long longValue() { + return value; + } + + public BitSet and(final BitSet bs) { + return longBitSet(value & bs.longValue()); + } + + public BitSet or(final BitSet bs) { + return longBitSet(value | bs.longValue()); + } + + public BitSet shiftRight(final int n) { + return longBitSet(value >> n); + } + + public BitSet shiftLeft(final int n) { + return longBitSet(value << n); + } + + public int bitsUsed() { + return toStream().length(); + } + + public int bitsOn() { + return toStream().foldLeft((acc, b) -> acc + (b ? 1 : 0), 0); + } + + /** + * Returns a stream of boolean where the head is the most significant bit + * (the bit with the largest value) + */ + public Stream toStream() { + return Stream.fromString(Long.toBinaryString(value)).map(BitSet::toBoolean).dropWhile(b -> !b); + } + + @Override + public String toString() { + return Show.bitSetShow.showS(this); + } + + @Override + public boolean equals(Object obj) { + return Equal.equals0(BitSet.class, this, obj, Equal.bitSetSequal); + } + + public int bitsToRight(final int index) { + if (index >= MAX_BIT_SIZE) { + throw new IllegalArgumentException("Does not support an index " + + "greater than or equal to " + MAX_BIT_SIZE + " bits, actual argument is " + index); + } + int pos = index - 1; + long mask = BASE_LONG << (pos); + int result = 0; + while (pos >= 0) { + if ((mask & value) != 0) { + result++; + } + mask = mask >> 1; + pos--; + } + return result; + } + + public List toList() { + return toStream().toList(); + } + + public A foldRight(final F2 f, A acc) { + return toStream().foldRight(b -> p -> f.f(b, p._1()), acc); + } + + public A foldLeft(final F2 f, A acc) { + return toStream().foldLeft(f, acc); + } + + public BitSet xor(final BitSet bs) { + return longBitSet(value ^ bs.longValue()); + } + + public BitSet not() { + return longBitSet(~value); + } + + public BitSet takeLower(final int n) { + return streamBitSet(toStream().reverse().take(n).reverse()); + } + + public BitSet takeUpper(final int n) { + String zero = Integer.toString(FALSE_BIT); + String current = asString(); + String pad = Monoid.stringMonoid.sumLeft(List.replicate(MAX_BIT_SIZE - current.length(), zero)); + return stringBitSet(pad + current.substring(0, Math.max(n - pad.length(), 0))); + } + + /** + * Returns the bit set from indices in the range from low (inclusive) + * to high(exclusive) from the least significant bit (on the right), + * e.g. "101101".range(1, 4) == "0110" + */ + public BitSet range(final int highIndex, final int lowIndex) { + int max = Math.max(lowIndex, highIndex); + int min = Math.min(lowIndex, highIndex); + return new BitSet(max == min ? 0L : (value << (64 - max)) >>> (64 - max + min)); + } + + public static boolean toBoolean(final char c) { + return c != '0'; + } + + public static boolean toBoolean(final int i) { + return i != FALSE_BIT; + } + + public static int toInt(final boolean b) { + return b ? TRUE_BIT : FALSE_BIT; + } + + public String asString() { + return Long.toBinaryString(value); + } +} diff --git a/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java b/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java new file mode 100644 index 00000000..371f5435 --- /dev/null +++ b/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java @@ -0,0 +1,218 @@ +package fj.data.hamt; + +import fj.Equal; +import fj.F2; +import fj.Hash; +import fj.Ord; +import fj.P2; +import fj.Show; +import fj.data.List; +import fj.data.Option; +import fj.data.Seq; +import fj.data.Stream; + +import static fj.P.p; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.hamt.BitSet.longBitSet; + +/** + * A hash array mapped trie (HAMT) is an implementation of an associative + * array that combines the characteristics of a hash table and an array + * mapped trie. It is a refined version of the more general notion of + * a hash tree. + * + * Based on "Ideal Hash Trees" by Phil Bagwell, available from + * http://lampwww.epfl.ch/papers/idealhashtrees.pdf + */ +public final class HashArrayMappedTrie { + + private final Seq> seq; + private final BitSet bitSet; + private final Hash hash; + private final Equal equal; + + public static final int BITS_IN_INDEX = 5; + public static final int SIZE = (int) StrictMath.pow(2, BITS_IN_INDEX); + public static final int MIN_INDEX = 0; + public static final int MAX_INDEX = SIZE - 1; + + /** + * Creates an empty trie for the bitset, sequence of nodes, equal and hash. + * + * @param bs - The set of bits to indicate which of the SIZE nodes in the sequence are used. + * @param s - The sequence of HAMT nodes - either a HAMT or a key-value pair. + * @param e - Equality instance for keys. + * @param h - Hash instance for keys. + */ + private HashArrayMappedTrie(final BitSet bs, final Seq> s, final Equal e, final Hash h) { + bitSet = bs; + seq = s; + hash = h; + equal = e; + } + + /** + * Creates an empty trie. + */ + public static HashArrayMappedTrie empty(final Equal e, final Hash h) { + return new HashArrayMappedTrie<>(BitSet.empty(), Seq.empty(), e, h); + } + + /** + * Create and empty trie keyed by integer. + */ + public static HashArrayMappedTrie emptyKeyInteger() { + return empty(Equal.intEqual, Hash.intHash); + } + + /** + * Returns if the trie is empty. + */ + public boolean isEmpty() { + return bitSet.isEmpty(); + } + + /** + * Static constructor for a HAMT instance. + */ + private static HashArrayMappedTrie hamt(final BitSet bs, final Seq> s, final Equal e, final Hash h) { + return new HashArrayMappedTrie<>(bs, s, e, h); + } + + /** + * Returns an optional value for the given key k. + */ + public Option find(final K k) { + return find(k, MIN_INDEX, MIN_INDEX + BITS_IN_INDEX); + } + + /** + * Returns an optional value for the given key k for those nodes between + * lowIndex (inclusive) and highIndex (exclusive). + */ + public Option find(final K k, final int lowIndex, final int highIndex) { + BitSet bs1 = longBitSet(hash.hash(k)).range(lowIndex, highIndex); + int i = (int) bs1.longValue(); + boolean b = bitSet.isSet(i); + final int index = bitSet.bitsToRight(i); + if (!b) { + return none(); + } else { + final Node oldNode = seq.index(index); + return oldNode.match( + n -> equal.eq(n._1(), k) ? some(n._2()) : none(), + hamt -> hamt.find(k, lowIndex + BITS_IN_INDEX, highIndex + BITS_IN_INDEX) + ); + } + } + + /** + * Adds the key-value pair (k, v) to the trie. + */ + public HashArrayMappedTrie set(final K k, final V v) { + return set(k, v, MIN_INDEX, MIN_INDEX + BITS_IN_INDEX); + } + + /** + * Adds the product of key-value (k, v) pairs to the trie. + */ + public HashArrayMappedTrie set(final List> list) { + return list.foldLeft(h -> p -> h.set(p._1(), p._2()), this); + } + + /** + * Sets the key-value pair (k, v) for the bit range lowIndex (inclusive) to highIndex (exclusive). + */ + private HashArrayMappedTrie set(final K k, final V v, final int lowIndex, final int highIndex) { + final BitSet bs1 = longBitSet(hash.hash(k)).range(lowIndex, highIndex); + final int i = (int) bs1.longValue(); + final boolean b = bitSet.isSet(i); + final int index = bitSet.bitsToRight(i); + + if (!b) { + // append new node + final Node sn1 = Node.p2Node(p(k, v)); + return hamt(bitSet.set(i), seq.insert(index, sn1), equal, hash); + } else { + final Node oldNode = seq.index(index); + final Node newNode = oldNode.match(n -> { + if (equal.eq(n._1(), k)) { + return Node.p2Node(p(k, v)); + } else { + final HashArrayMappedTrie e = HashArrayMappedTrie.empty(equal, hash); + final HashArrayMappedTrie h1 = e.set(n._1(), n._2(), lowIndex + BITS_IN_INDEX, highIndex + BITS_IN_INDEX); + final HashArrayMappedTrie h2 = h1.set(k, v, lowIndex + BITS_IN_INDEX, highIndex + BITS_IN_INDEX); + return Node.hamtNode(h2); + } + }, hamt -> Node.hamtNode(hamt.set(k, v, lowIndex + BITS_IN_INDEX, highIndex + BITS_IN_INDEX)) + ); + return hamt(bitSet, seq.update(index, newNode), equal, hash); + } + } + + /** + * Returns a stream of key-value pairs. + */ + public Stream> toStream() { + return seq.toStream().bind(Node::toStream); + } + + /** + * Returns the list of key-value pairs, ordered by key. + */ + public List> toList(Ord o) { + return toStream().sort(Ord.p2Ord1(o)).toList(); + } + + /** + * Returns a list of key-value pairs. + */ + public List> toList() { + return toStream().toList(); + } + + @Override + public String toString() { + return Show.hamtShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + /** + * Performs a left-fold reduction across this trie. + */ + public B foldLeftOnNode(F2, B> f, B b) { + return seq.foldLeft(f, b); + } + + /** + * Performs a left-fold reduction across this trie. + */ + public B foldLeft(F2, B> f, F2, B> g, B b) { + return foldLeftOnNode((acc, n) -> n.match(p -> f.f(acc, p), h -> g.f(acc, h)), b); + } + + /** + * Performs a left-fold reduction across this trie. + */ + public B foldLeft(F2, B> f, B b) { + return foldLeftOnNode((acc, n) -> n.match(p -> f.f(acc, p), h -> h.foldLeft(f, acc)), b); + } + + public BitSet getBitSet() { + return bitSet; + } + + public Seq> getSeq() { + return seq; + } + + /** + * Returns the number of elements in the trie. + */ + public int length() { + return seq.foldLeft( + (acc, node) -> node.match(p2 -> acc + 1, hamt -> acc + hamt.length()), 0 + ); + } + +} diff --git a/core/src/main/java/fj/data/hamt/Node.java b/core/src/main/java/fj/data/hamt/Node.java new file mode 100644 index 00000000..af453925 --- /dev/null +++ b/core/src/main/java/fj/data/hamt/Node.java @@ -0,0 +1,54 @@ +package fj.data.hamt; + +import fj.F; +import fj.P2; +import fj.Show; +import fj.data.Either; +import fj.data.Option; +import fj.data.Stream; + +/** + * A Hash Array Mapped Trie node that is either a key-value pair or a + * Hash Array Mapped Trie. + */ +public final class Node { + + private final Either, HashArrayMappedTrie> either; + + public Node(final Either, HashArrayMappedTrie> e) { + either = e; + } + + public Node(final P2 simpleNode) { + this(Either.left(simpleNode)); + } + + public Node(final HashArrayMappedTrie hamt) { + this(Either.right(hamt)); + } + + public static Node p2Node(final P2 p) { + return new Node<>(p); + } + + public static Node hamtNode(final HashArrayMappedTrie hamt) { + return new Node<>(hamt); + } + + /** + * Performs a reduction on this Node using the given arguments. + */ + public B match(final F, B> f, final F, B> g) { + return either.either(f, g); + } + + public Stream> toStream() { + return match(Stream::single, HashArrayMappedTrie::toStream); + } + + @Override + public String toString() { + return Show.hamtNodeShow(Show.anyShow(), Show.anyShow()).showS(this); + } + +} diff --git a/core/src/main/java/fj/data/hlist/HList.java b/core/src/main/java/fj/data/hlist/HList.java index b02d9504..8e9b5389 100644 --- a/core/src/main/java/fj/data/hlist/HList.java +++ b/core/src/main/java/fj/data/hlist/HList.java @@ -28,15 +28,13 @@ public abstract class HList> { public abstract Apply, HCons> extender(); - private static final HNil nil = new HNil(); - /** * Returns the empty list. * * @return the empty list. */ public static HNil nil() { - return nil; + return HNil.nil; } /** @@ -47,7 +45,7 @@ public static HNil nil() { * @return a heterogeneous list consisting of an element and another list. */ public static > HCons cons(final E e, final L l) { - return new HCons(e, l); + return new HCons<>(e, l); } /** @@ -91,7 +89,7 @@ public C append(final A a, final B b) { * @return a method for concatenating lists to the empty list. */ public static > HAppend append() { - return new HAppend((hNil, l) -> l); + return new HAppend<>((hNil, l) -> l); } /** @@ -102,7 +100,7 @@ public static > HAppend append() { */ public static , B, C extends HList, H extends HAppend> HAppend, B, HCons> append(final H h) { - return new HAppend, B, HCons>((c, l) -> cons(c.head(), h.append(c.tail(), l))); + return new HAppend<>((c, l) -> cons(c.head(), h.append(c.tail(), l))); } } @@ -212,7 +210,7 @@ private HFoldr(final F3 foldRight) { * @return a fold instance for the empty list. */ public static HFoldr hFoldr() { - return new HFoldr((f, v, hNil) -> v); + return new HFoldr<>((f, v, hNil) -> v); } /** @@ -234,7 +232,7 @@ public static HFoldr hFoldr() { H extends HFoldr, PP extends Apply, RR>> HFoldr, RR> hFoldr(final PP p, final H h) { - return new HFoldr, RR>((f, v, c) -> p.apply(f, P.p(c.head(), h.foldRight(f, v, c.tail())))); + return new HFoldr<>((f, v, c) -> p.apply(f, P.p(c.head(), h.foldRight(f, v, c.tail())))); } /** @@ -285,7 +283,10 @@ public HCons> extend(final X e) { * The empty list */ public static final class HNil extends HList { - HNil() { + + private static final HNil nil = new HNil(); + + private HNil() { } public HCons extend(final E e) { diff --git a/core/src/main/java/fj/data/hlist/HPre.java b/core/src/main/java/fj/data/hlist/HPre.java index a37c3b78..6c6174a4 100644 --- a/core/src/main/java/fj/data/hlist/HPre.java +++ b/core/src/main/java/fj/data/hlist/HPre.java @@ -1,6 +1,5 @@ package fj.data.hlist; -import fj.F; import fj.Show; /** @@ -58,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 @@ -93,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/Fold.java b/core/src/main/java/fj/data/optic/Fold.java index 94b8aaf6..691c779c 100644 --- a/core/src/main/java/fj/data/optic/Fold.java +++ b/core/src/main/java/fj/data/optic/Fold.java @@ -39,7 +39,7 @@ public final List getAll(final S s) { /** find the first target of a {@link Fold} matching the predicate */ public final F> find(final F p) { - return foldMap(Monoid.optionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); + return foldMap(Monoid.firstOptionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); } /** get the first target of a {@link Fold} */ @@ -76,7 +76,7 @@ public final Fold composeFold(final Fold other) { return new Fold() { @Override public F foldMap(final Monoid m, final F f) { - return Fold.this. foldMap(m, other. foldMap(m, f)); + return Fold.this.foldMap(m, other. foldMap(m, f)); } }; } @@ -110,7 +110,7 @@ public static Fold id() { return PIso. pId().asFold(); } - public static final Fold, A> codiagonal() { + public static Fold, A> codiagonal() { return new Fold, A>() { @Override public F, B> foldMap(final Monoid m, final F f) { diff --git a/core/src/main/java/fj/data/optic/Getter.java b/core/src/main/java/fj/data/optic/Getter.java index b5c60d50..a5c9fa99 100644 --- a/core/src/main/java/fj/data/optic/Getter.java +++ b/core/src/main/java/fj/data/optic/Getter.java @@ -94,11 +94,11 @@ public static Getter id() { return PIso. pId().asGetter(); } - public static final Getter, A> codiagonal() { + public static Getter, A> codiagonal() { return getter(e -> e.either(Function.identity(), Function.identity())); } - public static final Getter getter(final F get) { + public static Getter getter(final F get) { return new Getter() { @Override diff --git a/core/src/main/java/fj/data/optic/Iso.java b/core/src/main/java/fj/data/optic/Iso.java index 71ca7359..4b41b1ad 100644 --- a/core/src/main/java/fj/data/optic/Iso.java +++ b/core/src/main/java/fj/data/optic/Iso.java @@ -47,32 +47,32 @@ public Iso, P2> second() { /**********************************************************/ /** compose an {@link Iso} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pIso.composeSetter(other.pSetter)); } /** compose an {@link Iso} with a {@link Traversal} */ - public final Traversal composeTraversal(final Traversal other) { + public Traversal composeTraversal(final Traversal other) { return new Traversal<>(pIso.composeTraversal(other.pTraversal)); } /** compose an {@link Iso} with a {@link Optional} */ - public final Optional composeOptional(final Optional other) { + public Optional composeOptional(final Optional other) { return new Optional<>(pIso.composeOptional(other.pOptional)); } /** compose an {@link Iso} with a {@link Prism} */ - public final Prism composePrism(final Prism other) { + public Prism composePrism(final Prism other) { return new Prism<>(pIso.composePrism(other.pPrism)); } /** compose an {@link Iso} with a {@link Lens} */ - public final Lens composeLens(final Lens other) { + public Lens composeLens(final Lens other) { return asLens().composeLens(other); } /** compose an {@link Iso} with an {@link Iso} */ - public final Iso composeIso(final Iso other) { + public Iso composeIso(final Iso other) { return new Iso<>(pIso.composeIso(other.pIso)); } @@ -82,31 +82,31 @@ public final Iso composeIso(final Iso other) { /** view an {@link Iso} as a {@link Setter} */ @Override - public final Setter asSetter() { + public Setter asSetter() { return new Setter<>(pIso.asSetter()); } /** view an {@link Iso} as a {@link Traversal} */ @Override - public final Traversal asTraversal() { + public Traversal asTraversal() { return new Traversal<>(pIso.asTraversal()); } /** view an {@link Iso} as a {@link Optional} */ @Override - public final Optional asOptional() { + public Optional asOptional() { return new Optional<>(pIso.asOptional()); } /** view an {@link Iso} as a {@link Prism} */ @Override - public final Prism asPrism() { + public Prism asPrism() { return new Prism<>(pIso.asPrism()); } /** view an {@link Iso} as a {@link Lens} */ @Override - public final Lens asLens() { + public Lens asLens() { return new Lens<>(pIso.asLens()); } @@ -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/Lens.java b/core/src/main/java/fj/data/optic/Lens.java index bb65952b..ceea42c9 100644 --- a/core/src/main/java/fj/data/optic/Lens.java +++ b/core/src/main/java/fj/data/optic/Lens.java @@ -94,7 +94,7 @@ public F modify(final F f) { } /** join two {@link Lens} with the same target */ - public final Lens, A> sum(final Lens other) { + public Lens, A> sum(final Lens other) { return new Lens<>(pLens.sum(other.pLens)); } @@ -105,34 +105,34 @@ public final Lens, A> sum(final Lens other) { /** * compose a {@link Lens} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pLens.composeSetter(other.pSetter)); } /** * compose a {@link Lens} with a {@link Traversal} */ - public final Traversal composeTraversal(final Traversal other) { + public Traversal composeTraversal(final Traversal other) { return new Traversal<>(pLens.composeTraversal(other.pTraversal)); } /** compose a {@link Lens} with an {@link Optional} */ - public final Optional composeOptional(final Optional other) { + public Optional composeOptional(final Optional other) { return new Optional<>(pLens.composeOptional(other.pOptional)); } /** compose a {@link Lens} with a {@link Prism} */ - public final Optional composePrism(final Prism other) { + public Optional composePrism(final Prism other) { return new Optional<>(pLens.composePrism(other.pPrism)); } /** compose a {@link Lens} with a {@link Lens} */ - public final Lens composeLens(final Lens other) { + public Lens composeLens(final Lens other) { return new Lens<>(pLens.composeLens(other.pLens)); } /** compose a {@link Lens} with an {@link Iso} */ - public final Lens composeIso(final Iso other) { + public Lens composeIso(final Iso other) { return new Lens<>(pLens.composeIso(other.pIso)); } @@ -148,17 +148,17 @@ public Setter asSetter() { /** view a {@link Lens} as a {@link Traversal} */ @Override - public final Traversal asTraversal() { + public Traversal asTraversal() { return new Traversal<>(pLens.asTraversal()); } /** view a {@link Lens} as an {@link Optional} */ @Override - public final Optional asOptional() { + public Optional asOptional() { return new Optional<>(pLens.asOptional()); } - public static final Lens id() { + public static Lens id() { return new Lens<>(PLens.pId()); } diff --git a/core/src/main/java/fj/data/optic/Optional.java b/core/src/main/java/fj/data/optic/Optional.java index de63fc2d..b2106cae 100644 --- a/core/src/main/java/fj/data/optic/Optional.java +++ b/core/src/main/java/fj/data/optic/Optional.java @@ -103,17 +103,17 @@ public Option getOption(final S s) { } /** join two {@link Optional} with the same target */ - public final Optional, A> sum(final Optional other) { + public Optional, A> sum(final Optional other) { return new Optional<>(pOptional.sum(other.pOptional)); } @Override - public final Optional, P2> first() { + public Optional, P2> first() { return new Optional<>(pOptional.first()); } @Override - public final Optional, P2> second() { + public Optional, P2> second() { return new Optional<>(pOptional.second()); } @@ -122,32 +122,32 @@ public final Optional, P2> second() { /**************************************************************/ /** compose a {@link Optional} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pOptional.composeSetter(other.pSetter)); } /** compose a {@link Optional} with a {@link Traversal} */ - public final Traversal composeTraversal(final Traversal other) { + public Traversal composeTraversal(final Traversal other) { return new Traversal<>(pOptional.composeTraversal(other.pTraversal)); } /** compose a {@link Optional} with a {@link Optional} */ - public final Optional composeOptional(final Optional other) { + public Optional composeOptional(final Optional other) { return new Optional<>(pOptional.composeOptional(other.pOptional)); } /** compose a {@link Optional} with a {@link Prism} */ - public final Optional composePrism(final Prism other) { + public Optional composePrism(final Prism other) { return new Optional<>(pOptional.composePrism(other.pPrism)); } /** compose a {@link Optional} with a {@link Lens} */ - public final Optional composeLens(final Lens other) { + public Optional composeLens(final Lens other) { return new Optional<>(pOptional.composeLens(other.pLens)); } /** compose a {@link Optional} with an {@link Iso} */ - public final Optional composeIso(final Iso other) { + public Optional composeIso(final Iso other) { return new Optional<>(pOptional.composeIso(other.pIso)); } @@ -157,13 +157,13 @@ public final Optional composeIso(final Iso other) { /** view a {@link Optional} as a {@link Setter} */ @Override - public final Setter asSetter() { + public Setter asSetter() { return new Setter<>(pOptional.asSetter()); } /** view a {@link Optional} as a {@link Traversal} */ @Override - public final Traversal asTraversal() { + public Traversal asTraversal() { return new Traversal<>(pOptional.asTraversal()); } @@ -171,12 +171,16 @@ public static Optional id() { return new Optional<>(POptional.pId()); } + public static Optional ignored() { + return optional(s -> Option.none(), a -> s -> s); + } + public static final Optional optional(final F> getOption, final F> set) { return new Optional<>(new POptional() { @Override public Either getOrModify(final S s) { - return getOption.f(s).option(Either.left(s), Either. right_()); + return getOption.f(s).option(Either.left(s), Either.right_()); } @Override @@ -209,7 +213,7 @@ public F> modifyEitherF(final F> f) { public F> modifyIOF(final F> f) { return s -> getOption.f(s).option( IOFunctions.unit(s), - a -> IOFunctions. map(f.f(a), b -> set.f(b).f(s)) + a -> IOFunctions.map(f.f(a), b -> set.f(b).f(s)) ); } diff --git a/core/src/main/java/fj/data/optic/PIso.java b/core/src/main/java/fj/data/optic/PIso.java index 850dedb6..b97b416f 100644 --- a/core/src/main/java/fj/data/optic/PIso.java +++ b/core/src/main/java/fj/data/optic/PIso.java @@ -6,6 +6,7 @@ import fj.P; import fj.P1; import fj.P2; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.data.Either; @@ -20,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
      *     |           |                                   |           |
      *     |           |                                   |           |
    @@ -39,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} @@ -130,7 +131,7 @@ public final F set(final B b) { } /** pair two disjoint {@link PIso} */ - public PIso, P2, P2, P2> product(final PIso other) { + public final PIso, P2, P2, P2> product(final PIso other) { return pIso( ss1 -> P.p(get(ss1._1()), other.get(ss1._2())), bb1 -> P.p(reverseGet(bb1._1()), other.reverseGet(bb1._2()))); @@ -314,7 +315,7 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { + public F> modifyValidationF(Semigroup s, final F> f) { return self.modifyValidationF(f); } @@ -510,7 +511,7 @@ public F> modifyV2F(final F> f) { } /** create a {@link PIso} using a pair of functions: one to get the target and one to get the source. */ - public static final PIso pIso(final F get, final F reverseGet) { + public static PIso pIso(final F get, final F reverseGet) { return new PIso() { @Override @@ -551,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 ade9b9e7..4dcad20b 100644 --- a/core/src/main/java/fj/data/optic/PLens.java +++ b/core/src/main/java/fj/data/optic/PLens.java @@ -4,6 +4,7 @@ import fj.Function; import fj.Monoid; import fj.P1; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.data.Either; @@ -16,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} @@ -320,7 +323,7 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { + public F> modifyValidationF(Semigroup s, final F> f) { return self.modifyValidationF(f); } @@ -418,7 +421,7 @@ public F modify(final F f) { }; } - public static final PLens pId() { + public static PLens pId() { return PIso. pId().asLens(); } @@ -499,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 b1b0fa6e..c70aa4c1 100644 --- a/core/src/main/java/fj/data/optic/POptional.java +++ b/core/src/main/java/fj/data/optic/POptional.java @@ -6,6 +6,7 @@ import fj.P; import fj.P1; import fj.P2; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.control.parallel.Strategy; @@ -19,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} @@ -345,7 +348,7 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { + public F> modifyValidationF(Semigroup s, final F> f) { return self.modifyValidationF(f); } @@ -366,7 +369,7 @@ public static POptional pId() { } /** create a {@link POptional} using the canonical functions: getOrModify and set */ - public static final POptional pOptional(final F> getOrModify, final F> set) { + public static POptional pOptional(final F> getOrModify, final F> set) { return new POptional() { @Override public Either getOrModify(final S s) { @@ -403,7 +406,7 @@ public F> modifyEitherF(final F> f) { public F> modifyIOF(final F> f) { return s -> getOrModify.f(s).either( IOFunctions::unit, - t -> IOFunctions. map(f.f(t), b -> set.f(b).f(s)) + t -> IOFunctions.map(f.f(t), b -> set.f(b).f(s)) ); } @@ -458,7 +461,7 @@ public F> modifyP1F(final F> f) { @Override public F> modifyValidationF(final F> f) { return s -> getOrModify.f(s).either( - t -> Validation. success(t), + Validation::success, t -> f.f(t).map(b -> set.f(b).f(s)) ); } @@ -478,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 476758a7..ffa87ba6 100644 --- a/core/src/main/java/fj/data/optic/PPrism.java +++ b/core/src/main/java/fj/data/optic/PPrism.java @@ -5,6 +5,7 @@ import fj.Monoid; import fj.P; import fj.P1; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.control.parallel.Strategy; @@ -18,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} @@ -126,7 +130,7 @@ public final F> modifyP1F(final F> f) { /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ public final F> modifyValidationF(final F> f) { return s -> getOrModify(s).either( - t -> Validation. success(t), + Validation::success, t -> f.f(t).map(this::reverseGet) ); } @@ -310,7 +314,7 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { + public F> modifyValidationF(Semigroup s, final F> f) { return self.modifyValidationF(f); } @@ -435,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 ad8e3ea8..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} @@ -84,11 +86,11 @@ public static PSetter pId() { return PIso. pId().asSetter(); } - public static final PSetter, Either, S, T> pCodiagonal() { + public static PSetter, Either, S, T> pCodiagonal() { return pSetter(f -> e -> e.bimap(f, f)); } - public static final PSetter pSetter(final F, F> modify) { + public static PSetter pSetter(final F, F> modify) { return new PSetter() { @Override public F modify(final F f) { diff --git a/core/src/main/java/fj/data/optic/PTraversal.java b/core/src/main/java/fj/data/optic/PTraversal.java index 8e98cf2f..6087f707 100644 --- a/core/src/main/java/fj/data/optic/PTraversal.java +++ b/core/src/main/java/fj/data/optic/PTraversal.java @@ -10,6 +10,7 @@ import fj.Monoid; import fj.P; import fj.P1; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.data.Either; @@ -23,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} @@ -82,7 +83,7 @@ public abstract class PTraversal { /** * modify polymorphically the target of a {@link PTraversal} with an Applicative function */ - public abstract F> modifyValidationF(F> f); + public abstract F> modifyValidationF(Semigroup s, F> f); /** * modify polymorphically the target of a {@link PTraversal} with an Applicative function @@ -104,7 +105,7 @@ public final List
    getAll(final S s) { /** find the first target of a {@link PTraversal} matching the predicate */ public final F> find(final F p) { - return foldMap(Monoid.optionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); + return foldMap(Monoid.firstOptionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); } /** get the first target of a {@link PTraversal} */ @@ -218,10 +219,10 @@ public F, V2>> modifyV2F(final F> f) { } @Override - public F, Validation>> modifyValidationF(final F> f) { + public F, Validation>> modifyValidationF(Semigroup se, final F> f) { return ss1 -> ss1.either( - s -> self.modifyValidationF(f).f(s).map(Either.left_()), - s1 -> other.modifyValidationF(f).f(s1).map(Either.right_()) + s -> self.modifyValidationF(se, f).f(s).map(Either.left_()), + s1 -> other.modifyValidationF(se, f).f(s1).map(Either.right_()) ); } @@ -307,8 +308,8 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { - return self.modifyValidationF(other.modifyValidationF(f)); + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(s, other.modifyValidationF(s, f)); } @Override @@ -450,7 +451,7 @@ public F, V2>> modifyV2F(final F> f) { } @Override - public F, Validation>> modifyValidationF(final F> f) { + public F, Validation>> modifyValidationF(Semigroup se, final F> f) { return s -> s.bimap(f, f).either( v -> v.map(Either.left_()), v -> v.map(Either.right_()) @@ -520,8 +521,8 @@ public F> modifyV2F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { - return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + public F> modifyValidationF(Semigroup se, final F> f) { + return s -> f.f(get2.f(s)).accumapply(se, f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); } @Override @@ -533,7 +534,7 @@ public F foldMap(final Monoid monoid, final F f) { public static PTraversal pTraversal(final F get1, final F get2, final F get3, final F4 set) { - return fromCurried(pTraversal(get1, get2, (b1, b2, s) -> (b3 -> set.f(b1, b2, b3, s))), get3); + return fromCurried(pTraversal(get1, get2, (b1, b2, s) -> b3 -> set.f(b1, b2, b3, s)), get3); } public static PTraversal pTraversal(final F get1, final F get2, final F get3, @@ -611,8 +612,8 @@ public F> modifyV2F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { - return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyValidationF(f).f(s)); + public F> modifyValidationF(Semigroup se, final F> f) { + return s -> f.f(lastGet.f(s)).accumapply(se, curriedTraversal.modifyValidationF(se, f).f(s)); } @Override diff --git a/core/src/main/java/fj/data/optic/Prism.java b/core/src/main/java/fj/data/optic/Prism.java index 409bfc6d..c2160b1f 100644 --- a/core/src/main/java/fj/data/optic/Prism.java +++ b/core/src/main/java/fj/data/optic/Prism.java @@ -35,32 +35,32 @@ public Option getOption(final S s) { /***********************************************************/ /** compose a {@link Prism} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pPrism.composeSetter(other.pSetter)); } /** compose a {@link Prism} with a {@link Traversal} */ - public final Traversal composeTraversal(final Traversal other) { + public Traversal composeTraversal(final Traversal other) { return new Traversal<>(pPrism.composeTraversal(other.pTraversal)); } /** compose a {@link Prism} with a {@link Optional} */ - public final Optional composeOptional(final Optional other) { + public Optional composeOptional(final Optional other) { return new Optional<>(pPrism.composeOptional(other.pOptional)); } /** compose a {@link Prism} with a {@link Lens} */ - public final Optional composeLens(final Lens other) { + public Optional composeLens(final Lens other) { return new Optional<>(pPrism.composeLens(other.pLens)); } /** compose a {@link Prism} with a {@link Prism} */ - public final Prism composePrism(final Prism other) { + public Prism composePrism(final Prism other) { return new Prism<>(pPrism.composePrism(other.pPrism)); } /** compose a {@link Prism} with an {@link Iso} */ - public final Prism composeIso(final Iso other) { + public Prism composeIso(final Iso other) { return new Prism<>(pPrism.composeIso(other.pIso)); } @@ -70,19 +70,19 @@ public final Prism composeIso(final Iso other) { /** view a {@link Prism} as a {@link Setter} */ @Override - public final Setter asSetter() { + public Setter asSetter() { return new Setter<>(pPrism.asSetter()); } /** view a {@link Prism} as a {@link Traversal} */ @Override - public final Traversal asTraversal() { + public Traversal asTraversal() { return new Traversal<>(pPrism.asTraversal()); } /** view a {@link Prism} as a {@link Optional} */ @Override - public final Optional asOptional() { + public Optional asOptional() { return new Optional<>(pPrism.asOptional()); } @@ -95,7 +95,7 @@ public static Prism prism(final F> getOption, final F< @Override public Either getOrModify(final S s) { - return getOption.f(s).option(Either.left(s), Either. right_()); + return getOption.f(s).option(Either.left(s), Either.right_()); } @Override diff --git a/core/src/main/java/fj/data/optic/Setter.java b/core/src/main/java/fj/data/optic/Setter.java index 40fc8473..95227f10 100644 --- a/core/src/main/java/fj/data/optic/Setter.java +++ b/core/src/main/java/fj/data/optic/Setter.java @@ -23,7 +23,7 @@ public F set(final A b) { } /** join two {@link Setter} with the same target */ - public final Setter, A> sum(final Setter other) { + public Setter, A> sum(final Setter other) { return new Setter<>(pSetter.sum(other.pSetter)); } @@ -32,17 +32,17 @@ public final Setter, A> sum(final Setter other) { /************************************************************/ /** compose a {@link Setter} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pSetter.composeSetter(other.pSetter)); } /** compose a {@link Setter} with a {@link Traversal} */ - public final Setter composeTraversal(final Traversal other) { + public Setter composeTraversal(final Traversal other) { return new Setter<>(pSetter.composeTraversal(other.pTraversal)); } /** compose a {@link Setter} with an {@link Iso} */ - public final Setter composeIso(final Iso other) { + public Setter composeIso(final Iso other) { return new Setter<>(pSetter.composeIso(other.pIso)); } @@ -50,12 +50,12 @@ public static Setter id() { return new Setter<>(PSetter.pId()); } - public static final Setter, S> codiagonal() { + public static Setter, S> codiagonal() { return new Setter<>(PSetter.pCodiagonal()); } /** alias for {@link PSetter} constructor with a monomorphic modify function */ - public static final Setter setter(final F, F> modify) { + public static Setter setter(final F, F> modify) { return new Setter<>(PSetter.pSetter(modify)); } } diff --git a/core/src/main/java/fj/data/optic/Traversal.java b/core/src/main/java/fj/data/optic/Traversal.java index 756a95ed..a1a466d8 100644 --- a/core/src/main/java/fj/data/optic/Traversal.java +++ b/core/src/main/java/fj/data/optic/Traversal.java @@ -8,6 +8,7 @@ import fj.F7; import fj.Monoid; import fj.P1; +import fj.Semigroup; import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.data.Either; @@ -72,8 +73,8 @@ public F> modifyP1F(final F> f) { } @Override - public F> modifyValidationF(final F> f) { - return pTraversal.modifyValidationF(f); + public F> modifyValidationF(Semigroup s, final F> f) { + return pTraversal.modifyValidationF(s, f); } @Override @@ -87,7 +88,7 @@ public F foldMap(final Monoid monoid, final F f) { } /** join two {@link Traversal} with the same target */ - public final Traversal, A> sum(final Traversal other) { + public Traversal, A> sum(final Traversal other) { return new Traversal<>(pTraversal.sum(other.pTraversal)); } @@ -96,12 +97,12 @@ public final Traversal, A> sum(final Traversal other) /***************************************************************/ /** compose a {@link Traversal} with a {@link Setter} */ - public final Setter composeSetter(final Setter other) { + public Setter composeSetter(final Setter other) { return new Setter<>(pTraversal.composeSetter(other.pSetter)); } /** compose a {@link Traversal} with a {@link Traversal} */ - public final Traversal composeTraversal(final Traversal other) { + public Traversal composeTraversal(final Traversal other) { return new Traversal<>(pTraversal.composeTraversal(other.pTraversal)); } @@ -111,7 +112,7 @@ public final Traversal composeTraversal(final Traversal other) { /** view a {@link Traversal} as a {@link Setter} */ @Override - public final Setter asSetter() { + public Setter asSetter() { return new Setter<>(pTraversal.asSetter()); } 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/data/vector/V.java b/core/src/main/java/fj/data/vector/V.java index 84481d96..70d60f58 100644 --- a/core/src/main/java/fj/data/vector/V.java +++ b/core/src/main/java/fj/data/vector/V.java @@ -7,7 +7,6 @@ import fj.F5; import fj.P; import fj.P1; -import fj.P2; /** * Functions across vectors. @@ -36,7 +35,7 @@ public static V2 v(final A a1, final A a2) { * @return The vector-2. */ public static V2 v(final F0 a1, final F0 a2) { - return V2.p(P.lazy(() -> a1.f(), () -> a2.f())); + return V2.p(P.lazy(a1, a2)); } /** @@ -45,7 +44,7 @@ public static V2 v(final F0 a1, final F0 a2) { * @return A function that puts elements in a vector-2. */ public static F2> v2() { - return (a, a1) -> v(a, a1); + return V::v; } /** @@ -78,7 +77,7 @@ public static V3 v(final P1 a1, final F0 a2, final F0 a3) { * @return A function that puts elements in a vector-3. */ public static F3> v3() { - return (a, a1, a2) -> v(a, a1, a2); + return V::v; } /** @@ -113,7 +112,7 @@ public static V4 v(final P1 a1, final P1 a2, final F0 a3, final * @return A function that puts elements in a vector-4. */ public static F4> v4() { - return (a, a1, a2, a3) -> v(a, a1, a2, a3); + return V::v; } @@ -151,7 +150,7 @@ public static V5 v(final P1 a1, final P1 a2, final P1 a3, final * @return A function that puts elements in a vector-5. */ public static F5> v5() { - return (a, a1, a2, a3, a4) -> v(a, a1, a2, a3, a4); + return V::v; } } diff --git a/core/src/main/java/fj/data/vector/V2.java b/core/src/main/java/fj/data/vector/V2.java index e5bf7bc1..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. * @@ -30,7 +40,7 @@ private V2(final P2 inner) { * @return A new vector-2. */ public static V2 p(final P2 p) { - return new V2(p); + return new V2<>(p); } /** @@ -57,7 +67,7 @@ public A _2() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V2::_1; } /** @@ -66,7 +76,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V2::_2; } /** @@ -111,7 +121,7 @@ public Stream toStream() { * @return a function that transforms a vector-2 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V2::toStream; } /** @@ -120,7 +130,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-2 to the equivalent product-2. */ public static F, P2> p_() { - return v -> v.p(); + return V2::p; } /** @@ -193,7 +203,7 @@ public V2> vzip(final V2 bs) { * @return the first element of this vector as a product-1. */ public P1 head() { - return P.lazy(() -> V2.this._1()); + return P.lazy(V2.this::_1); } } diff --git a/core/src/main/java/fj/data/vector/V3.java b/core/src/main/java/fj/data/vector/V3.java index 22e11946..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. * @@ -30,9 +40,9 @@ private V3(final P1 head, final V2 tail) { * @return A new vector-3. */ public static V3 p(final P3 p) { - return new V3( - P.lazy(() -> p._1()), - V2.p(P.lazy(() -> p._2(), () -> p._3())) + return new V3<>( + P.lazy(p::_1), + V2.p(P.lazy(p::_2, p::_3)) ); } @@ -44,7 +54,7 @@ public static V3 p(final P3 p) { * @return The new vector. */ public static V3 cons(final P1 head, final V2 tail) { - return new V3(head, tail); + return new V3<>(head, tail); } /** @@ -131,7 +141,7 @@ public Array toArray() { * @return A new vector after zipping the given vector of functions over this vector. */ public V3 apply(final V3> vf) { - return new V3(head.apply(vf.head()), tail.apply(vf.tail())); + return new V3<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -202,7 +212,7 @@ public Stream toStream() { * @return A new vector after the given function has been applied to each element. */ public V3 map(final F f) { - return new V3(head().map(f), tail().map(f)); + return new V3<>(head().map(f), tail().map(f)); } /** @@ -211,7 +221,7 @@ public V3 map(final F f) { * @return a function that transforms a vector-3 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V3::toStream; } /** @@ -220,7 +230,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-3 to the equivalent product-3. */ public static F, P3> p_() { - return v -> v.p(); + return V3::p; } /** @@ -229,7 +239,7 @@ public static F, P3> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V3::_1; } /** @@ -238,7 +248,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V3::_2; } /** @@ -247,7 +257,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V3::_3; } } \ No newline at end of file diff --git a/core/src/main/java/fj/data/vector/V4.java b/core/src/main/java/fj/data/vector/V4.java index 19ea2648..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. * @@ -30,20 +40,20 @@ private V4(final P1 head, final V3 tail) { * @return A new vector-4. */ public static V4 p(final P4 p) { - return new V4(P.lazy(() -> p._1()), - V3.p(new P3() { - public A _1() { - return p._2(); - } - - public A _2() { - return p._3(); - } - - public A _3() { - return p._4(); - } - })); + return new V4<>(P.lazy(p::_1), + V3.p(new P3() { + public A _1() { + return p._2(); + } + + public A _2() { + return p._3(); + } + + public A _3() { + return p._4(); + } + })); } /** @@ -54,7 +64,7 @@ public A _3() { * @return The new vector. */ public static V4 cons(final P1 head, final V3 tail) { - return new V4(head, tail); + return new V4<>(head, tail); } /** @@ -160,7 +170,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), () -> tail.toStream()); + return Stream.cons(head._1(), tail::toStream); } /** @@ -180,7 +190,7 @@ public Array toArray() { * @return A new vector after the given function has been applied to each element. */ public V4 map(final F f) { - return new V4(head.map(f), tail.map(f)); + return new V4<>(head.map(f), tail.map(f)); } /** @@ -190,7 +200,7 @@ public V4 map(final F f) { * @return A new vector after zipping the given vector of functions over this vector. */ public V4 apply(final V4> vf) { - return new V4(head.apply(vf.head()), tail.apply(vf.tail())); + return new V4<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -233,7 +243,7 @@ public V4> vzip(final V4 bs) { * @return a function that transforms a vector-4 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V4::toStream; } /** @@ -242,7 +252,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-4 to the equivalent product-4. */ public static F, P4> p_() { - return v -> v.p(); + return V4::p; } /** @@ -251,7 +261,7 @@ public static F, P4> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V4::_1; } /** @@ -260,7 +270,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V4::_2; } /** @@ -269,7 +279,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V4::_3; } /** @@ -278,6 +288,6 @@ public static F, A> __3() { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return v -> v._4(); + return V4::_4; } } diff --git a/core/src/main/java/fj/data/vector/V5.java b/core/src/main/java/fj/data/vector/V5.java index 3aab13e3..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. * @@ -30,24 +40,24 @@ private V5(final P1 head, final V4 tail) { * @return A new vector-5. */ public static V5 p(final P5 p) { - return new V5(P.lazy(() -> p._1()), - V4.p(new P4() { - public A _1() { - return p._2(); - } - - public A _2() { - return p._3(); - } - - public A _3() { - return p._4(); - } - - public A _4() { - return p._5(); - } - })); + return new V5<>(P.lazy(p::_1), + V4.p(new P4() { + public A _1() { + return p._2(); + } + + public A _2() { + return p._3(); + } + + public A _3() { + return p._4(); + } + + public A _4() { + return p._5(); + } + })); } /** @@ -58,7 +68,7 @@ public A _4() { * @return The new vector. */ public static V5 cons(final P1 head, final V4 tail) { - return new V5(head, tail); + return new V5<>(head, tail); } /** @@ -177,7 +187,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), () -> tail.toStream()); + return Stream.cons(head._1(), tail::toStream); } /** @@ -197,7 +207,7 @@ public Array toArray() { * @return A new vector after the given function has been applied to each element. */ public V5 map(final F f) { - return new V5(head.map(f), tail.map(f)); + return new V5<>(head.map(f), tail.map(f)); } /** @@ -207,7 +217,7 @@ public V5 map(final F f) { * @return A new vector after zipping the given vector of functions over this vector. */ public V5 apply(final V5> vf) { - return new V5(head.apply(vf.head()), tail.apply(vf.tail())); + return new V5<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -250,7 +260,7 @@ public V5> vzip(final V5 bs) { * @return a function that transforms a vector-5 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V5::toStream; } /** @@ -259,7 +269,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-5 to the equivalent product-5. */ public static F, P5> p_() { - return v -> v.p(); + return V5::p; } /** @@ -268,7 +278,7 @@ public static F, P5> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V5::_1; } /** @@ -277,7 +287,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V5::_2; } /** @@ -286,7 +296,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V5::_3; } /** @@ -295,7 +305,7 @@ public static F, A> __3() { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return v -> v._4(); + return V5::_4; } /** @@ -304,6 +314,6 @@ public static F, A> __4() { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return v -> v._5(); + return V5::_5; } } diff --git a/core/src/main/java/fj/data/vector/V6.java b/core/src/main/java/fj/data/vector/V6.java index 66a5afca..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. * @@ -30,28 +40,28 @@ private V6(final P1 head, final V5 tail) { * @return A new vector-6. */ public static V6 p(final P6 p) { - return new V6(P.lazy(() -> p._1()), - V5.p(new P5() { - public A _1() { - return p._2(); - } - - public A _2() { - return p._3(); - } - - public A _3() { - return p._4(); - } - - public A _4() { - return p._5(); - } - - public A _5() { - return p._6(); - } - })); + return new V6<>(P.lazy(p::_1), + V5.p(new P5() { + public A _1() { + return p._2(); + } + + public A _2() { + return p._3(); + } + + public A _3() { + return p._4(); + } + + public A _4() { + return p._5(); + } + + public A _5() { + return p._6(); + } + })); } /** @@ -62,7 +72,7 @@ public A _5() { * @return The new vector. */ public static V6 cons(final P1 head, final V5 tail) { - return new V6(head, tail); + return new V6<>(head, tail); } /** @@ -194,7 +204,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), () -> tail.toStream()); + return Stream.cons(head._1(), tail::toStream); } /** @@ -214,7 +224,7 @@ public Array toArray() { * @return A new vector after the given function has been applied to each element. */ public V6 map(final F f) { - return new V6(head.map(f), tail.map(f)); + return new V6<>(head.map(f), tail.map(f)); } /** @@ -224,7 +234,7 @@ public V6 map(final F f) { * @return A new vector after zipping the given vector of functions over this vector. */ public V6 apply(final V6> vf) { - return new V6(head.apply(vf.head()), tail.apply(vf.tail())); + return new V6<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -267,7 +277,7 @@ public V6> vzip(final V6 bs) { * @return a function that transforms a vector-6 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V6::toStream; } /** @@ -276,7 +286,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-6 to the equivalent product-6. */ public static F, P6> p_() { - return v -> v.p(); + return V6::p; } /** @@ -285,7 +295,7 @@ public static F, P6> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V6::_1; } /** @@ -294,7 +304,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V6::_2; } /** @@ -303,7 +313,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V6::_3; } /** @@ -312,7 +322,7 @@ public static F, A> __3() { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return v -> v._4(); + return V6::_4; } /** @@ -321,7 +331,7 @@ public static F, A> __4() { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return v -> v._5(); + return V6::_5; } /** @@ -330,7 +340,7 @@ public static F, A> __5() { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return v -> v._6(); + return V6::_6; } } diff --git a/core/src/main/java/fj/data/vector/V7.java b/core/src/main/java/fj/data/vector/V7.java index c57e3551..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. * @@ -30,32 +40,32 @@ private V7(final P1 head, final V6 tail) { * @return A new vector-7. */ public static V7 p(final P7 p) { - return new V7(P.lazy(() -> p._1()), - V6.p(new P6() { - public A _1() { - return p._2(); - } - - public A _2() { - return p._3(); - } - - public A _3() { - return p._4(); - } - - public A _4() { - return p._5(); - } - - public A _5() { - return p._6(); - } - - public A _6() { - return p._7(); - } - })); + return new V7<>(P.lazy(p::_1), + V6.p(new P6() { + public A _1() { + return p._2(); + } + + public A _2() { + return p._3(); + } + + public A _3() { + return p._4(); + } + + public A _4() { + return p._5(); + } + + public A _5() { + return p._6(); + } + + public A _6() { + return p._7(); + } + })); } /** @@ -66,7 +76,7 @@ public A _6() { * @return The new vector. */ public static V7 cons(final P1 head, final V6 tail) { - return new V7(head, tail); + return new V7<>(head, tail); } /** @@ -211,7 +221,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), () -> tail.toStream()); + return Stream.cons(head._1(), tail::toStream); } /** @@ -231,7 +241,7 @@ public Array toArray() { * @return A new vector after the given function has been applied to each element. */ public V7 map(final F f) { - return new V7(head.map(f), tail.map(f)); + return new V7<>(head.map(f), tail.map(f)); } /** @@ -241,7 +251,7 @@ public V7 map(final F f) { * @return A new vector after zipping the given vector of functions over this vector. */ public V7 apply(final V7> vf) { - return new V7(head.apply(vf.head()), tail.apply(vf.tail())); + return new V7<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -284,7 +294,7 @@ public V7> vzip(final V7 bs) { * @return a function that transforms a vector-7 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V7::toStream; } /** @@ -293,7 +303,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-7 to the equivalent product-7. */ public static F, P7> p_() { - return v -> v.p(); + return V7::p; } /** @@ -302,7 +312,7 @@ public static F, P7> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V7::_1; } /** @@ -311,7 +321,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V7::_2; } /** @@ -320,7 +330,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V7::_3; } /** @@ -329,7 +339,7 @@ public static F, A> __3() { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return v -> v._4(); + return V7::_4; } /** @@ -338,7 +348,7 @@ public static F, A> __4() { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return v -> v._5(); + return V7::_5; } /** @@ -347,7 +357,7 @@ public static F, A> __5() { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return v -> v._6(); + return V7::_6; } /** @@ -356,7 +366,7 @@ public static F, A> __6() { * @return a function that gets the seventh element of a given vector. */ public static F, A> __7() { - return v -> v._7(); + return V7::_7; } } diff --git a/core/src/main/java/fj/data/vector/V8.java b/core/src/main/java/fj/data/vector/V8.java index 72af03f1..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. * @@ -30,36 +40,36 @@ private V8(final P1 head, final V7 tail) { * @return A new vector-8. */ public static V8 p(final P8 p) { - return new V8(P.lazy(() -> p._1()), - V7.p(new P7() { - public A _1() { - return p._2(); - } + return new V8<>(P.lazy(p::_1), + V7.p(new P7() { + public A _1() { + return p._2(); + } - public A _2() { - return p._3(); - } + public A _2() { + return p._3(); + } - public A _3() { - return p._4(); - } + public A _3() { + return p._4(); + } - public A _4() { - return p._5(); - } + public A _4() { + return p._5(); + } - public A _5() { - return p._6(); - } + public A _5() { + return p._6(); + } - public A _6() { - return p._7(); - } + public A _6() { + return p._7(); + } - public A _7() { - return p._8(); - } - })); + public A _7() { + return p._8(); + } + })); } /** @@ -70,7 +80,7 @@ public A _7() { * @return The new vector. */ public static V8 cons(final P1 head, final V7 tail) { - return new V8(head, tail); + return new V8<>(head, tail); } /** @@ -228,7 +238,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), () -> tail.toStream()); + return Stream.cons(head._1(), tail::toStream); } /** @@ -248,7 +258,7 @@ public Array toArray() { * @return A new vector after the given function has been applied to each element. */ public V8 map(final F f) { - return new V8(head.map(f), tail.map(f)); + return new V8<>(head.map(f), tail.map(f)); } /** @@ -258,7 +268,7 @@ public V8 map(final F f) { * @return A new vector after zipping the given vector of functions over this vector. */ public V8 apply(final V8> vf) { - return new V8(head.apply(vf.head()), tail.apply(vf.tail())); + return new V8<>(head.apply(vf.head()), tail.apply(vf.tail())); } /** @@ -301,7 +311,7 @@ public V8> vzip(final V8 bs) { * @return a function that transforms a vector-8 to a stream of its elements. */ public static F, Stream> toStream_() { - return v -> v.toStream(); + return V8::toStream; } /** @@ -310,7 +320,7 @@ public static F, Stream> toStream_() { * @return a function that transforms a vector-8 to the equivalent product-8. */ public static F, P8> p_() { - return v -> v.p(); + return V8::p; } @@ -320,7 +330,7 @@ public static F, P8> p_() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return v -> v._1(); + return V8::_1; } /** @@ -329,7 +339,7 @@ public static F, A> __1() { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return v -> v._2(); + return V8::_2; } /** @@ -338,7 +348,7 @@ public static F, A> __2() { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return v -> v._3(); + return V8::_3; } /** @@ -347,7 +357,7 @@ public static F, A> __3() { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return v -> v._4(); + return V8::_4; } /** @@ -356,7 +366,7 @@ public static F, A> __4() { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return v -> v._5(); + return V8::_5; } /** @@ -365,7 +375,7 @@ public static F, A> __5() { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return v -> v._6(); + return V8::_6; } /** @@ -374,7 +384,7 @@ public static F, A> __6() { * @return a function that gets the seventh element of a given vector. */ public static F, A> __7() { - return v -> v._7(); + return V8::_7; } /** @@ -383,7 +393,7 @@ public static F, A> __7() { * @return a function that gets the eighth element of a given vector. */ public static F, A> __8() { - return v -> v._8(); + return V8::_8; } } diff --git a/core/src/main/java/fj/function/BigIntegers.java b/core/src/main/java/fj/function/BigIntegers.java index b60d29f4..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() { @@ -22,40 +20,40 @@ private BigIntegers() { * Curried Integer addition. */ public static final F> add = - curry((a1, a2) -> a1.add(a2)); + curry((F2) BigInteger::add); /** * Curried Integer multiplication. */ public static final F> multiply = - curry((a1, a2) -> a1.multiply(a2)); + curry(BigInteger::multiply); /** * Curried Integer subtraction. */ public static final F> subtract = - curry((a1, a2) -> a1.subtract(a2)); + curry((F2) BigInteger::subtract); /** * Negation. */ - public static final F negate = i -> i.negate(); + public static final F negate = BigInteger::negate; /** * Absolute value. */ - public static final F abs = i -> i.abs(); + public static final F abs = BigInteger::abs; /** * Remainder. */ public static final F> remainder = - curry((a1, a2) -> a1.remainder(a2)); + curry(BigInteger::remainder); /** * Power. */ - public static final F> power = curry((a1, a2) -> a1.pow(a2)); + public static final F> power = curry(BigInteger::pow); /** * Sums a list of big integers. 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/Characters.java b/core/src/main/java/fj/function/Characters.java index 8aebde7b..220bea7a 100644 --- a/core/src/main/java/fj/function/Characters.java +++ b/core/src/main/java/fj/function/Characters.java @@ -1,7 +1,6 @@ package fj.function; import fj.F; -import fj.F2; import static fj.Function.curry; @@ -13,30 +12,30 @@ private Characters() { throw new UnsupportedOperationException(); } public static final F toString = c -> Character.toString(c); - public static final F isLowerCase = ch -> Character.isLowerCase(ch); - public static final F isUpperCase = ch -> Character.isUpperCase(ch); - public static final F isTitleCase = ch -> Character.isTitleCase(ch); - public static final F isDigit = ch -> Character.isDigit(ch); - public static final F isDefined = ch -> Character.isDefined(ch); - public static final F isLetter = ch -> Character.isLetter(ch); - public static final F isLetterOrDigit = ch -> Character.isLetterOrDigit(ch); - public static final F isJavaIdentifierStart = ch -> Character.isJavaIdentifierStart(ch); - public static final F isJavaIdentifierPart = ch -> Character.isJavaIdentifierPart(ch); - public static final F isUnicodeIdentifierStart = ch -> Character.isUnicodeIdentifierStart(ch); - public static final F isUnicodeIdentifierPart = ch -> Character.isUnicodeIdentifierPart(ch); - public static final F isIdentifierIgnorable = ch -> Character.isIdentifierIgnorable(ch); - public static final F toLowerCase = ch -> Character.toLowerCase(ch); - public static final F toUpperCase = ch -> Character.toUpperCase(ch); - public static final F toTitleCase = ch -> Character.toTitleCase(ch); + public static final F isLowerCase = Character::isLowerCase; + public static final F isUpperCase = Character::isUpperCase; + public static final F isTitleCase = Character::isTitleCase; + public static final F isDigit = Character::isDigit; + public static final F isDefined = Character::isDefined; + public static final F isLetter = Character::isLetter; + public static final F isLetterOrDigit = Character::isLetterOrDigit; + public static final F isJavaIdentifierStart = Character::isJavaIdentifierStart; + public static final F isJavaIdentifierPart = Character::isJavaIdentifierPart; + public static final F isUnicodeIdentifierStart = Character::isUnicodeIdentifierStart; + public static final F isUnicodeIdentifierPart = Character::isUnicodeIdentifierPart; + public static final F isIdentifierIgnorable = Character::isIdentifierIgnorable; + public static final F toLowerCase = Character::toLowerCase; + public static final F toUpperCase = Character::toUpperCase; + public static final F toTitleCase = Character::toTitleCase; public static final F> digit = curry((ch, radix) -> Character.digit(ch, radix)); - public static final F getNumericValue = ch -> Character.getNumericValue(ch); - public static final F isSpaceChar = ch -> Character.isSpaceChar(ch); - public static final F isWhitespace = ch -> Character.isWhitespace(ch); - public static final F isISOControl = ch -> Character.isISOControl(ch); - public static final F getType = ch -> Character.getType(ch); - public static final F getDirectionality = ch -> Character.getDirectionality(ch); - public static final F isMirrored = ch -> Character.isMirrored(ch); - public static final F reverseBytes = ch -> Character.reverseBytes(ch); + public static final F getNumericValue = Character::getNumericValue; + public static final F isSpaceChar = Character::isSpaceChar; + public static final F isWhitespace = Character::isWhitespace; + public static final F isISOControl = Character::isISOControl; + public static final F getType = Character::getType; + public static final F getDirectionality = Character::getDirectionality; + public static final F isMirrored = Character::isMirrored; + public static final F reverseBytes = Character::reverseBytes; public static final F isNewLine = c -> c == '\n'; } diff --git a/core/src/main/java/fj/function/Doubles.java b/core/src/main/java/fj/function/Doubles.java index a15a4042..aefcbfdc 100644 --- a/core/src/main/java/fj/function/Doubles.java +++ b/core/src/main/java/fj/function/Doubles.java @@ -1,22 +1,16 @@ package fj.function; import fj.F; -import fj.F2; import fj.Monoid; import fj.data.List; import fj.data.Option; import static fj.Function.curry; -import static fj.Semigroup.doubleAdditionSemigroup; -import static fj.Semigroup.doubleMultiplicationSemigroup; import static fj.data.Option.none; import static fj.data.Option.some; -import static java.lang.Math.abs; /** * Curried functions over Doubles. - * - * @version %build.number% */ public final class Doubles { private Doubles() { @@ -26,17 +20,17 @@ private Doubles() { /** * Curried Double addition. */ - public static final F> add = doubleAdditionSemigroup.sum(); + public static final F> add = x -> y -> x + y; /** * Curried Double multiplication. */ - public static final F> multiply = doubleMultiplicationSemigroup.sum(); + public static final F> multiply = x -> y -> x * y; /** * Curried Double subtraction. */ - public static final F> subtract = curry((x, y) -> x - y); + public static final F> subtract = x -> y -> x - y; /** * Negation. @@ -46,17 +40,17 @@ private Doubles() { /** * Absolute value. */ - public static final F abs = x -> abs(x); + public static final F abs = Math::abs; /** * Remainder. */ - public static final F> remainder = curry((a, b) -> a % b); + public static final F> remainder = x -> y -> x % y; /** * Power. */ - public static final F> power = curry((a, b) -> StrictMath.pow(a, b)); + public static final F> power = x -> y -> StrictMath.pow(x, y); /** * Evenness. @@ -70,7 +64,7 @@ private Doubles() { * @return The sum of the doubless in the list. */ public static double sum(final List doubles) { - return Monoid.doubleAdditionMonoid.sumLeft(doubles); + return doubles.foldLeft((x, y) -> x + y, 0.0); } /** @@ -80,7 +74,7 @@ public static double sum(final List doubles) { * @return The product of the doubles in the list. */ public static double product(final List doubles) { - return Monoid.doubleMultiplicationMonoid.sumLeft(doubles); + return doubles.foldLeft((x, y) -> x * y, 1.0); } /** diff --git a/core/src/main/java/fj/function/Effect0.java b/core/src/main/java/fj/function/Effect0.java index c5d4fe50..7644d394 100644 --- a/core/src/main/java/fj/function/Effect0.java +++ b/core/src/main/java/fj/function/Effect0.java @@ -1,10 +1,36 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ +import fj.*; + +import static fj.Unit.unit; + 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(); + }); + } + } diff --git a/core/src/main/java/fj/function/Effect1.java b/core/src/main/java/fj/function/Effect1.java index 8c89b715..1f3be909 100644 --- a/core/src/main/java/fj/function/Effect1.java +++ b/core/src/main/java/fj/function/Effect1.java @@ -1,10 +1,76 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ -public interface Effect1 { +import fj.F; +import fj.P; +import fj.P1; +import fj.Unit; + +import java.util.function.Consumer; + +import static fj.Unit.unit; + +public interface Effect1 extends Consumer { void f(A a); + 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); + return unit(); + }; + } + + 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); + } + + 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)); + } + + + } diff --git a/core/src/main/java/fj/function/Effect2.java b/core/src/main/java/fj/function/Effect2.java index c0735043..b9a5ad5a 100644 --- a/core/src/main/java/fj/function/Effect2.java +++ b/core/src/main/java/fj/function/Effect2.java @@ -1,10 +1,25 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ -public interface Effect2 { +import fj.F2; +import fj.Unit; + +import java.util.function.BiConsumer; + +import static fj.Unit.unit; + +public interface Effect2 extends BiConsumer { void f(A a, B b); + 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/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 10fd3b15..7f8fd0e8 100644 --- a/core/src/main/java/fj/function/Integers.java +++ b/core/src/main/java/fj/function/Integers.java @@ -1,7 +1,7 @@ package fj.function; import fj.F; -import fj.F2; + import static fj.Function.curry; import fj.Monoid; @@ -16,8 +16,6 @@ /** * Curried functions over Integers. - * - * @version %build.number% */ public final class Integers { private Integers() { @@ -47,7 +45,7 @@ private Integers() { /** * Absolute value. */ - public static final F abs = x -> abs(x); + public static final F abs = Math::abs; /** * Remainder. diff --git a/core/src/main/java/fj/function/Longs.java b/core/src/main/java/fj/function/Longs.java index 89c2babd..2762f3e2 100644 --- a/core/src/main/java/fj/function/Longs.java +++ b/core/src/main/java/fj/function/Longs.java @@ -1,17 +1,20 @@ package fj.function; import fj.F; -import fj.F2; +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; /** * Curried functions over Longs. - * - * @version %build.number% */ public final class Longs { private Longs() { @@ -41,11 +44,45 @@ private Longs() { /** * Absolute value. */ - public static final F abs = x -> abs(x); + public static final F abs = Math::abs; /** * Remainder. */ 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/main/java/fj/function/Strings.java b/core/src/main/java/fj/function/Strings.java index 88e9cc2f..d9815248 100644 --- a/core/src/main/java/fj/function/Strings.java +++ b/core/src/main/java/fj/function/Strings.java @@ -3,20 +3,23 @@ import fj.F; import fj.data.List; import fj.data.Stream; + +import java.util.regex.Pattern; + import static fj.Function.curry; import static fj.function.Booleans.not; import static fj.function.Characters.isWhitespace; /** * Curried string functions. - * - * @version %build.number% */ public final class Strings { private Strings() { throw new UnsupportedOperationException(); } + private static final Pattern lineSeparatorPattern = Pattern.compile("\\r?\\n"); + public static final String lineSeparator = System.getProperty("line.separator"); /** @@ -61,7 +64,7 @@ private Strings() { /** * A curried version of {@link String#length()}. */ - public static final F length = s -> s.length(); + public static final F length = String::length; /** * A curried version of {@link String#contains(CharSequence)}. @@ -76,21 +79,25 @@ private Strings() { public static final F> matches = curry((s1, s2) -> s2.matches(s1)); public static List lines(String s) { - return List.list(s.split("\\r?\\n")); + return List.list(lineSeparatorPattern.split(s)); } public static F> lines() { - return s -> lines(s); + return Strings::lines; } public static String unlines(List list) { StringBuilder sb = new StringBuilder(); - list.intersperse(lineSeparator).foreachDoEffect(s -> sb.append(s)); + list.intersperse(lineSeparator).foreachDoEffect(sb::append); return sb.toString(); } public static F, String> unlines() { - return l -> unlines(l); + return Strings::unlines; + } + + public static F reverse() { + return s -> new StringBuilder(s).reverse().toString(); } } diff --git a/core/src/main/java/fj/function/Try0.java b/core/src/main/java/fj/function/Try0.java index e218f0a7..7cdacfc1 100644 --- a/core/src/main/java/fj/function/Try0.java +++ b/core/src/main/java/fj/function/Try0.java @@ -1,16 +1,52 @@ 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. * * 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 { 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/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 a6c9cbee..1ccaa9af 100644 --- a/core/src/main/java/fj/function/TryEffect0.java +++ b/core/src/main/java/fj/function/TryEffect0.java @@ -1,10 +1,57 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ +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; + 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(); + }); + } + } 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 ebd18c03..ed0e7b53 100644 --- a/core/src/main/java/fj/function/Visitor.java +++ b/core/src/main/java/fj/function/Visitor.java @@ -3,7 +3,6 @@ import fj.Equal; import fj.F; import fj.F0; -import fj.F2; import fj.Function; import fj.Monoid; import fj.P1; @@ -16,8 +15,6 @@ /** * The essence of the visitor design pattern expressed polymorphically. - * - * @version %build.number% */ public final class Visitor { private Visitor() { @@ -43,7 +40,7 @@ public static X findFirst(final List> values, final F0 def) { * @return The first non-null value in the given list of optional values. If none is found return the given default value. */ public static X nullablefindFirst(final List values, final F0 def) { - return findFirst(values.map(Option.fromNull()), def); + return findFirst(values.map(Option.fromNull()), def); } /** @@ -57,7 +54,7 @@ public static X nullablefindFirst(final List values, final F0 def) { * given default. */ public static B visitor(final List>> visitors, final F0 def, final A value) { - return findFirst(visitors.map(Function.>apply(value)), def); + return findFirst(visitors.map(Function.apply(value)), def); } /** @@ -71,7 +68,7 @@ public static B visitor(final List>> visitors, final F0 * given default. */ public static B nullableVisitor(final List> visitors, final F0 def, final A value) { - return visitor(visitors.map(k -> compose(Option.fromNull(), k)), def, value); + return visitor(visitors.map(k -> compose(Option.fromNull(), k)), def, value); } /** 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 e5ce04c9..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; @@ -210,7 +208,7 @@ public Parser sequence(final Parser p) { * @return A new parser after function application. */ public Parser apply(final Parser, E> p) { - return p.bind((F, Parser>) f1 -> map(f1)); + return p.bind(this::map); } /** @@ -265,8 +263,8 @@ public Parser or(final Parser alt, final Semigroup s) { */ public Parser not(final F0 e) { return parser(i -> parse(i).isFail() ? - Validation.>success(result(i, unit())) : - Validation.>fail(e.f())); + Validation.success(result(i, unit())) : + Validation.fail(e.f())); } /** @@ -285,7 +283,7 @@ public Parser not(final E e) { * @return A parser that repeats application of this parser zero or many times. */ public Parser, E> repeat() { - return repeat1().or(() -> value(Stream.nil())); + return repeat1().or(() -> value(Stream.nil())); } /** @@ -345,7 +343,7 @@ public static Parser fail(final E e) { */ public static Parser, E> sequence(final List> ps) { return ps.isEmpty() ? - Parser., E>value(List.nil()) : + Parser.value(List.nil()) : ps.head().bind(a -> sequence(ps.tail()).map(cons_(a))); } @@ -365,8 +363,8 @@ private StreamParser() { */ public static Parser, I, E> element(final F0 e) { return parser(is -> is.isEmpty() ? - Validation., I>>fail(e.f()) : - Validation., I>>success(result(is.tail()._1(), is.head()))); + Validation.fail(e.f()) : + Validation.success(result(is.tail()._1(), is.head()))); } /** @@ -390,8 +388,8 @@ public static Parser, I, E> element(final E e) { public static Parser, I, E> satisfy(final F0 missing, final F sat, final F f) { return StreamParser.element(missing).bind(x -> f.f(x) ? - Parser., I, E>value(x) : - Parser., I, E>fail(sat.f(x))); + Parser.value(x) : + Parser.fail(sat.f(x))); } /** @@ -470,8 +468,8 @@ public static Parser, Character, E> character(final E miss */ public static Parser, Stream, E> characters(final F0 missing, final int n) { return n <= 0 ? - Parser., Stream, E>value(Stream.nil()) : - character(missing).bind(characters(missing, n - 1), Stream.cons_()); + Parser.value(Stream.nil()) : + character(missing).bind(characters(missing, n - 1), Stream.cons_()); } /** @@ -497,8 +495,8 @@ public static Parser, Stream, E> characters(fin final F sat, final Stream cs) { return cs.isEmpty() ? - Parser., Stream, E>value(Stream.nil()) : - character(missing, sat, cs.head()).bind(characters(missing, sat, cs.tail()._1()), Stream.cons_()); + Parser.value(Stream.nil()) : + character(missing, sat, cs.head()).bind(characters(missing, sat, cs.tail()._1()), Stream.cons_()); } /** @@ -549,7 +547,7 @@ public static Parser, String, E> string(final E missing, f * @return A parser that produces a digit (0 to 9). */ public static Parser, Digit, E> digit(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isDigit(c)).map(c1 -> Digit.fromChar(c1).some()); + return StreamParser.satisfy(missing, sat, Character::isDigit).map(c1 -> Digit.fromChar(c1).some()); } /** @@ -572,7 +570,7 @@ public static Parser, Digit, E> digit(final E missing, fin * @see Character#isLowerCase(char) */ public static Parser, Character, E> lower(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isLowerCase(c)); + return StreamParser.satisfy(missing, sat, Character::isLowerCase); } /** @@ -596,7 +594,7 @@ public static Parser, Character, E> lower(final E missing, * @see Character#isUpperCase(char) */ public static Parser, Character, E> upper(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isUpperCase(c)); + return StreamParser.satisfy(missing, sat, Character::isUpperCase); } /** @@ -620,7 +618,7 @@ public static Parser, Character, E> upper(final E missing, * @see Character#isDefined(char) */ public static Parser, Character, E> defined(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isDefined(c)); + return StreamParser.satisfy(missing, sat, Character::isDefined); } /** @@ -645,7 +643,7 @@ public static Parser, Character, E> defined(final E missin */ public static Parser, Character, E> highSurrogate(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isHighSurrogate(c)); + return StreamParser.satisfy(missing, sat, Character::isHighSurrogate); } /** @@ -671,7 +669,7 @@ public static Parser, Character, E> highSurrogate(final E */ public static Parser, Character, E> identifierIgnorable(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isIdentifierIgnorable(c)); + return StreamParser.satisfy(missing, sat, Character::isIdentifierIgnorable); } /** @@ -697,7 +695,7 @@ public static Parser, Character, E> identifierIgnorable(fi */ public static Parser, Character, E> isoControl(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isISOControl(c)); + return StreamParser.satisfy(missing, sat, Character::isISOControl); } /** @@ -722,7 +720,7 @@ public static Parser, Character, E> isoControl(final E mis */ public static Parser, Character, E> javaIdentifierPart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isJavaIdentifierPart(c)); + return StreamParser.satisfy(missing, sat, Character::isJavaIdentifierPart); } /** @@ -748,7 +746,7 @@ public static Parser, Character, E> javaIdentifierPart(fin */ public static Parser, Character, E> javaIdentifierStart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isJavaIdentifierStart(c)); + return StreamParser.satisfy(missing, sat, Character::isJavaIdentifierStart); } /** @@ -773,7 +771,7 @@ public static Parser, Character, E> javaIdentifierStart(fi * @see Character#isLetter(char) */ public static Parser, Character, E> alpha(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isLetter(c)); + return StreamParser.satisfy(missing, sat, Character::isLetter); } /** @@ -797,7 +795,7 @@ public static Parser, Character, E> alpha(final E missing, * @see Character#isLetterOrDigit(char) */ public static Parser, Character, E> alphaNum(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isLetterOrDigit(c)); + return StreamParser.satisfy(missing, sat, Character::isLetterOrDigit); } /** @@ -822,7 +820,7 @@ public static Parser, Character, E> alphaNum(final E missi */ public static Parser, Character, E> lowSurrogate(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isLowSurrogate(c)); + return StreamParser.satisfy(missing, sat, Character::isLowSurrogate); } /** @@ -846,7 +844,7 @@ public static Parser, Character, E> lowSurrogate(final E m * @see Character#isMirrored(char) */ public static Parser, Character, E> mirrored(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isMirrored(c)); + return StreamParser.satisfy(missing, sat, Character::isMirrored); } /** @@ -870,7 +868,7 @@ public static Parser, Character, E> mirrored(final E missi * @see Character#isSpace(char) */ public static Parser, Character, E> space(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isSpaceChar(c)); + return StreamParser.satisfy(missing, sat, Character::isSpaceChar); } /** @@ -895,7 +893,7 @@ public static Parser, Character, E> space(final E missing, */ public static Parser, Character, E> titleCase(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isTitleCase(c)); + return StreamParser.satisfy(missing, sat, Character::isTitleCase); } /** @@ -920,7 +918,7 @@ public static Parser, Character, E> titleCase(final E miss */ public static Parser, Character, E> unicodeIdentiferPart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isUnicodeIdentifierPart(c)); + return StreamParser.satisfy(missing, sat, Character::isUnicodeIdentifierPart); } /** @@ -946,7 +944,7 @@ public static Parser, Character, E> unicodeIdentiferPart(f */ public static Parser, Character, E> unicodeIdentiferStart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isUnicodeIdentifierStart(c)); + return StreamParser.satisfy(missing, sat, Character::isUnicodeIdentifierStart); } /** @@ -972,7 +970,7 @@ public static Parser, Character, E> unicodeIdentiferStart( */ public static Parser, Character, E> whitespace(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, c -> Character.isWhitespace(c)); + return StreamParser.satisfy(missing, sat, Character::isWhitespace); } /** diff --git a/core/src/main/java/fj/parser/Result.java b/core/src/main/java/fj/parser/Result.java index bcec7d8b..843dd558 100644 --- a/core/src/main/java/fj/parser/Result.java +++ b/core/src/main/java/fj/parser/Result.java @@ -1,7 +1,10 @@ package fj.parser; +import fj.Equal; import fj.F; -import fj.F2; +import fj.Hash; +import fj.Show; + import static fj.Function.curry; import java.util.Iterator; @@ -9,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; @@ -21,6 +22,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. * @@ -55,7 +72,7 @@ public Result mapRest(final F f) { * @return A first-class function mapping across the remainder of the parse input. */ public F, Result> mapRest() { - return f -> mapRest(f); + return this::mapRest; } /** @@ -74,7 +91,7 @@ public Result mapValue(final F f) { * @return A first-class function mapping across the parse value. */ public F, Result> mapValue() { - return f -> mapValue(f); + return this::mapValue; } /** @@ -94,7 +111,7 @@ public Result bimap(final F f, final F g) { * @return A first-class bifunctor map. */ public F, F, Result>> bimap() { - return curry((f, g) -> bimap(f, g)); + return curry(this::bimap); } /** @@ -133,7 +150,7 @@ public void remove() { * @return A result with the given remainder of the parse input and parse value. */ public static Result result(final I i, final A a) { - return new Result(i, a); + return new Result<>(i, a); } /** @@ -142,7 +159,7 @@ public static Result result(final I i, final A a) { * @return A first-class function for construction of a result. */ public static F>> result() { - return curry((i, a) -> result(i, a)); + return curry(Result::result); } } 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/ClassTest.java b/core/src/test/java/fj/ClassTest.java new file mode 100644 index 00000000..dfbc7f00 --- /dev/null +++ b/core/src/test/java/fj/ClassTest.java @@ -0,0 +1,53 @@ +package fj; + +import fj.data.List; +import fj.data.Natural; +import fj.data.Option; +import fj.data.Tree; +import org.junit.Test; + +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Iterator; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class ClassTest { + @Test + public void testInheritance() { + Class c = Class.clas(Natural.class); + List> l = c.inheritance(); + assertThat(l.length(), is(3)); + } + + @Test + public void testClassParameters() { + Class c = Class.clas(Option.none().getClass()); + Tree cp = c.classParameters(); + assertThat(cp.length(), is(1)); + } + + @Test + public void testSuperclassParameters() { + Class c = Class.clas(Option.none().getClass()); + Tree cp = c.superclassParameters(); + assertThat(cp.length(), is(2)); + } + + @Test + public void testInterfaceParameters() { + Class c = Class.clas(Option.none().getClass()); + List> l =c.interfaceParameters(); + assertThat(l.length(), is(0)); + } + + @Test + public void testTypeParameterTree() { + Class c = Class.clas(Option.none().getClass()); + Collection coll = c.classParameters().toCollection(); + for (Type t: coll) { + assertThat(Class.typeParameterTree(t).toString(), is("Tree(class fj.data.Option$None)")); + } + } +} diff --git a/core/src/test/java/fj/DigitTest.java b/core/src/test/java/fj/DigitTest.java new file mode 100644 index 00000000..13169aca --- /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.hamcrest.MatcherAssert.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/EqualTest.java b/core/src/test/java/fj/EqualTest.java new file mode 100644 index 00000000..bacecc46 --- /dev/null +++ b/core/src/test/java/fj/EqualTest.java @@ -0,0 +1,26 @@ +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 thenShouldWork() { + 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)); + assertThat(equalByLengthThenLastDigit.eq("str1", "strr1"), is(false)); + } +} diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java new file mode 100644 index 00000000..4f01a0db --- /dev/null +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -0,0 +1,30 @@ +package fj; + +import fj.data.Tree; +import fj.data.TreeZipper; +import org.junit.Test; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class FFunctionsTest { + + @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/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java new file mode 100644 index 00000000..68457f09 --- /dev/null +++ b/core/src/test/java/fj/MonoidTest.java @@ -0,0 +1,58 @@ +package fj; + +import fj.data.Enumerator; +import fj.data.Option; +import fj.data.Set; +import fj.data.Stream; +import org.junit.Test; + +import static fj.data.Option.some; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.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))); + } + + @Test + public void intersection_monoid_test() { + Bounded integersBounded = Bounded.bounded(0, 10); + 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); + assertThat(actual, is(Set.set(Ord.intOrd, 3, 4))); + } + + @Test + 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 actual = unionMonoid.sum(first, second); + 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); + Set set = Set.set(Ord.intOrd, 7, 8, 9, 10); + 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)); + } + +} diff --git a/core/src/test/java/fj/OrdTest.java b/core/src/test/java/fj/OrdTest.java new file mode 100644 index 00000000..4addec65 --- /dev/null +++ b/core/src/test/java/fj/OrdTest.java @@ -0,0 +1,45 @@ +package fj; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class OrdTest { + + @Test + public void isGreaterThan() { + F pred = Ord.longOrd.isGreaterThan(1L); + + assertThat(pred.f(0L), is(false)); + assertThat(pred.f(1L), is(false)); + assertThat(pred.f(2L), is(true)); + } + + @Test + public void isLessThan() { + F pred = Ord.longOrd.isLessThan(1L); + + assertThat(pred.f(0L), is(true)); + 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 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)); + assertThat(lengthThenLastDigitOrd.compare("str", "strr"), is(Ordering.LT)); + } +} diff --git a/core/src/test/java/fj/OrderingTest.java b/core/src/test/java/fj/OrderingTest.java new file mode 100644 index 00000000..640bc1d3 --- /dev/null +++ b/core/src/test/java/fj/OrderingTest.java @@ -0,0 +1,21 @@ +package fj; + +import org.hamcrest.Matcher; +import org.hamcrest.core.Is; +import org.junit.Test; + +import static fj.Ordering.EQ; +import static fj.Ordering.GT; +import static fj.Ordering.LT; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class OrderingTest { + + @Test + public void reverse() throws Exception { + assertThat(GT.reverse(), is(LT)); + assertThat(LT.reverse(), is(GT)); + assertThat(EQ.reverse(), is(EQ)); + } +} \ No newline at end of file diff --git a/core/src/test/java/fj/P1Test.java b/core/src/test/java/fj/P1Test.java index a866e0c2..4d3c5c35 100644 --- a/core/src/test/java/fj/P1Test.java +++ b/core/src/test/java/fj/P1Test.java @@ -11,7 +11,7 @@ public final class P1Test { @Test public void bug105() throws Exception { - final P1 p1 = P.p("Foo").memo(); + final P1 p1 = P.weakMemo(() -> "Foo"); final AtomicInteger nullCounter = new AtomicInteger(); ExecutorService executorService = Executors.newCachedThreadPool(); 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/PTest.java b/core/src/test/java/fj/PTest.java new file mode 100644 index 00000000..00220f9d --- /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.hamcrest.MatcherAssert.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)); + } +} 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))); + } +} diff --git a/core/src/test/java/fj/ShowTest.java b/core/src/test/java/fj/ShowTest.java index e271f5a1..3d6d8354 100644 --- a/core/src/test/java/fj/ShowTest.java +++ b/core/src/test/java/fj/ShowTest.java @@ -6,20 +6,11 @@ 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() { 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..6fd46fd1 --- /dev/null +++ b/core/src/test/java/fj/TryEffectTest.java @@ -0,0 +1,80 @@ +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.hamcrest.MatcherAssert.assertThat; + +public class TryEffectTest { + + @Test + public void testTryEffect0Success() { + F, Validation> 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, Validation> 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..46fb2dd3 --- /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.hamcrest.MatcherAssert.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/control/db/TestDbState.java b/core/src/test/java/fj/control/db/TestDbState.java new file mode 100644 index 00000000..2a942e1a --- /dev/null +++ b/core/src/test/java/fj/control/db/TestDbState.java @@ -0,0 +1,54 @@ +package fj.control.db; + +import fj.Unit; +import fj.data.Option; +import fj.function.Try1; +import org.apache.commons.dbutils.DbUtils; +import org.junit.Test; + +import java.sql.*; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class TestDbState { + @Test + public void testWriter() throws SQLException { + final int TEN = 10; + DbState writer = DbState.writer(DbState.driverManager("jdbc:h2:mem:")); + + DB setup = DB.db((Try1) c -> { + Statement s = null; + try { + s = c.createStatement(); + assertThat(s.executeUpdate("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))"), is(0)); + assertThat(s.executeUpdate("INSERT INTO TEST (ID, NAME) VALUES (" + TEN + ", 'FOO')"), is(1)); + } finally { + DbUtils.closeQuietly(s); + } + return Unit.unit(); + }); + + DB> query = new DB>() { + @Override + public Option run(Connection c) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = c.prepareStatement("SELECT ID FROM TEST WHERE NAME = ?"); + ps.setString(1, "FOO"); + rs = ps.executeQuery(); + if (rs.next()) { + return Option.some(rs.getInt("ID")); + } else { + return Option.none(); + } + } finally { + DbUtils.closeQuietly(rs); + DbUtils.closeQuietly(ps); + } + } + }; + assertThat(writer.run(setup.bind(v -> query)).some(), is(TEN)); + } +} 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..1f5e7bba --- /dev/null +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -0,0 +1,69 @@ +package fj.control.parallel; + +import fj.Ord; +import fj.P; +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; + +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.hamcrest.MatcherAssert.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 -> 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); + 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/ArrayTest.java b/core/src/test/java/fj/data/ArrayTest.java index e274a00a..11683fbf 100644 --- a/core/src/test/java/fj/data/ArrayTest.java +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -1,20 +1,20 @@ package fj.data; +import org.hamcrest.CoreMatchers; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + -/** - * Created by MarkPerry on 14 Feb 16. - */ public class ArrayTest { @Test - public void toJavaArray() { - final int max = 3; - List list = List.range(1, max + 1); - assertThat(list.toArray().toJavaArray(), equalTo(list.toJavaArray())); + public void array_is_safe() { + List list = List.range(1, 2); + + assertThat(list.toArray().array(Integer[].class), instanceOf(Integer[].class)); } } diff --git a/core/src/test/java/fj/data/BooleansTest.java b/core/src/test/java/fj/data/BooleansTest.java index b0d3f969..e5dbeb12 100644 --- a/core/src/test/java/fj/data/BooleansTest.java +++ b/core/src/test/java/fj/data/BooleansTest.java @@ -9,10 +9,9 @@ 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; + -/** - * Created by amar on 28/01/15. - */ public class BooleansTest { @Test @@ -73,7 +72,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/DListTest.java b/core/src/test/java/fj/data/DListTest.java new file mode 100644 index 00000000..77f4db6c --- /dev/null +++ b/core/src/test/java/fj/data/DListTest.java @@ -0,0 +1,32 @@ +package fj.data; + +import org.junit.Test; + +import static fj.data.DList.*; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.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())); + } +} 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 { + + +} 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 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..a3302e55 --- /dev/null +++ b/core/src/test/java/fj/data/EvalTest.java @@ -0,0 +1,83 @@ +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 + 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. + 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 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 { + + 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; + } + } +} diff --git a/core/src/test/java/fj/data/IOFunctionsTest.java b/core/src/test/java/fj/data/IOFunctionsTest.java new file mode 100644 index 00000000..8ae18b1a --- /dev/null +++ b/core/src/test/java/fj/data/IOFunctionsTest.java @@ -0,0 +1,123 @@ +package fj.data; + +import fj.*; +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +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.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +public class IOFunctionsTest { + + @Test + public void bracket_happy_path() throws Exception { + AtomicBoolean closed = new AtomicBoolean(); + Reader reader = new StringReader("Read OK") { + @Override + public void close() { + super.close(); + closed.set(true); + } + }; + + IO bracketed = IOFunctions.bracket( + () -> reader, + IOFunctions.closeReader, + r -> () -> new BufferedReader(r).readLine() + ); + + assertThat(bracketed.run(), is("Read OK")); + assertThat(closed.get(), is(true)); + } + + @Test + public void bracket_exception_path() throws Exception { + AtomicBoolean closed = new AtomicBoolean(); + Reader reader = new StringReader("Read OK") { + @Override + public void close() { + super.close(); + closed.set(true); + throw new IllegalStateException("Should be suppressed"); + } + }; + + IO bracketed = IOFunctions.bracket( + () -> reader, + IOFunctions.closeReader, + r -> () -> {throw new IllegalArgumentException("OoO");} + ); + + try { + bracketed.run(); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("OoO")); + } + 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("foo1bar2foobar3")); + } + + @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(); + F> f = this::println; + final F> upperCaseAndPrint = f.o().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 diff --git a/core/src/test/java/fj/data/JavaTest.java b/core/src/test/java/fj/data/JavaTest.java index 3b5a5785..b8d99510 100644 --- a/core/src/test/java/fj/data/JavaTest.java +++ b/core/src/test/java/fj/data/JavaTest.java @@ -6,17 +6,16 @@ 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. - */ public class JavaTest { @Test 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/LazyStringTest.java b/core/src/test/java/fj/data/LazyStringTest.java index 4f6325af..060c5394 100644 --- a/core/src/test/java/fj/data/LazyStringTest.java +++ b/core/src/test/java/fj/data/LazyStringTest.java @@ -4,11 +4,9 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.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..784b9510 100644 --- a/core/src/test/java/fj/data/ListBufferTest.java +++ b/core/src/test/java/fj/data/ListBufferTest.java @@ -3,11 +3,9 @@ import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.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 8725ef05..720157e8 100644 --- a/core/src/test/java/fj/data/ListTest.java +++ b/core/src/test/java/fj/data/ListTest.java @@ -1,20 +1,40 @@ 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.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; -/** - * Created by MarkPerry on 16/01/2015. - */ public class ListTest { @Test @@ -82,13 +102,199 @@ public void listReduce() { } @Test - public void toJavaArray() { + public void array() { final int max = 3; Integer[] ints = new Integer[max]; for (int i = 0; i < max; i++) { ints[i] = i + 1; }; - assertThat(List.range(1, max + 1).toJavaArray(), equalTo(ints)); + 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)))); + } } 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..eee5752d 100644 --- a/core/src/test/java/fj/data/List_Traverse_Tests.java +++ b/core/src/test/java/fj/data/List_Traverse_Tests.java @@ -7,11 +7,9 @@ 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; + -/** - * 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 5d2778e5..e03d7aae 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -1,15 +1,37 @@ package fj.data; -import org.junit.Assert; +import fj.P; +import fj.control.Trampoline; import org.junit.Test; -import static fj.data.Option.some; -import static org.junit.Assert.assertTrue; +import java.io.IOException; -/** - * Created by MarkPerry on 15/01/2015. - */ -public class OptionTest { +import static fj.Function.constant; +import static fj.Ord.*; +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.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.*; +import static org.junit.Assert.*; + +public final class OptionTest { @Test public void equals() { @@ -23,8 +45,389 @@ 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)); } + @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 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()) + .foreachDoEffect(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()) + .foreachDoEffect(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()) + .foreachDoEffect(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()) + .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())); + }); + + } + + @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()) + .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())); + }); + } + + @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()) + .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())); + }); + } + + @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()) + .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())); + }); + } + + @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()) + .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())); + }); + } + + @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()) + .foreachDoEffect(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()) + .foreachDoEffect(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()) + .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))); + }); + + } + + @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()) + .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))); + }); + } + + @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()) + .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))); + }); + } + + @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()) + .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))); + }); + } + + @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()) + .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))); + }); + } + + @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() { + assertEquals(left(none()), sequenceEitherLeft(none())); + 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("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("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("zero"))).run(), sequenceIO(some(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(List.single(none()), sequenceList(none())); + assertEquals(List.nil(), sequenceList(some(List.nil()))); + 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("zero")), sequenceOption(some(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(none()), sequenceP1(none())); + assertEquals(p(some("zero")), sequenceP1(some(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.single(none()), sequenceSeq(none())); + assertEquals(Seq.empty(), sequenceSeq(some(Seq.empty()))); + 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("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.single(none()), sequenceStream(none())); + assertEquals(Stream.nil(), sequenceStream(some(Stream.nil()))); + assertEquals(Stream.single(some("zero")), sequenceStream(some(Stream.single("zero")))); + assertEquals(Stream.arrayStream(some("zero"), some("one")), sequenceStream(some(Stream.arrayStream("zero", "one")))); + } + + @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 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)))); + assertEquals(left(some(0)), some("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(none()), none().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("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(none()), none().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("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("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + 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("zero").traverseOption(constant(none()))); + assertEquals(some(none()), none().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("zero").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + 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("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("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("zero").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + 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("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(success(none()), none().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("zero").traverseValidation(constant(Validation.success(0)))); + } } diff --git a/core/src/test/java/fj/data/SeqTest.java b/core/src/test/java/fj/data/SeqTest.java index 633ae321..8a78ab31 100644 --- a/core/src/test/java/fj/data/SeqTest.java +++ b/core/src/test/java/fj/data/SeqTest.java @@ -1,14 +1,38 @@ package fj.data; +import fj.P2; +import fj.control.Trampoline; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +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.*; -/** - * Created by MarkPerry on 16/01/2015. - */ public class SeqTest { @Test @@ -34,4 +58,233 @@ public void convertToString() { expected.append(')'); assertEquals(expected.toString(), Seq.seq(Array.range(0, 10000).array()).toString()); } + + + @Test + public void test() { + P2, Seq> p2 = Seq.single(1).split(5); + assertThat(p2._1(), is(Seq.single(1))); + assertThat(p2._2(), is(Seq.empty())); + } + + @Test + public void testBind() { + 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))); + } } diff --git a/core/src/test/java/fj/data/SetTest.java b/core/src/test/java/fj/data/SetTest.java index 78084bb4..e30d166a 100644 --- a/core/src/test/java/fj/data/SetTest.java +++ b/core/src/test/java/fj/data/SetTest.java @@ -2,13 +2,12 @@ import org.junit.Test; +import static fj.data.Option.none; +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; -/** - * Created by MarkPerry on 18/08/2015. - */ public class SetTest { @Test @@ -23,4 +22,18 @@ public void testString() { assertThat(s.toString(), equalTo("Set(1,2,3)")); } + @Test + public void testLookups() { + Set s = Set.set(intOrd, 5, 1, 7, 8); + assertThat(s.lookup(7), equalTo(some(7))); + assertThat(s.lookup(4), equalTo(none())); + assertThat(s.lookupLT(6), equalTo(some(5))); + assertThat(s.lookupLT(1), equalTo(none())); + assertThat(s.lookupGT(5), equalTo(some(7))); + assertThat(s.lookupGT(9), equalTo(none())); + assertThat(s.lookupLE(8), equalTo(some(8))); + assertThat(s.lookupLE(0), equalTo(none())); + assertThat(s.lookupGE(8), equalTo(some(8))); + assertThat(s.lookupGE(9), equalTo(none())); + } } diff --git a/core/src/test/java/fj/data/StateTest.java b/core/src/test/java/fj/data/StateTest.java index 9fc8a9fb..479efc38 100644 --- a/core/src/test/java/fj/data/StateTest.java +++ b/core/src/test/java/fj/data/StateTest.java @@ -2,14 +2,44 @@ import org.junit.Test; -/** - * Created by MarkPerry on 18/12/2014. - */ +import static fj.P.p; +import static org.junit.Assert.assertEquals; + 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"; + } } diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index a37764c2..febd4567 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -1,27 +1,49 @@ package fj.data; +import fj.Equal; +import fj.P2; +import fj.control.Trampoline; 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.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; +import static org.hamcrest.MatcherAssert.assertThat; -/** - * Created by Zheka Kozlov on 27.05.2015. - */ 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 +70,271 @@ 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 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))); + } } diff --git a/core/src/test/java/fj/data/TreeMapTest.java b/core/src/test/java/fj/data/TreeMapTest.java index d3e60936..051f0773 100644 --- a/core/src/test/java/fj/data/TreeMapTest.java +++ b/core/src/test/java/fj/data/TreeMapTest.java @@ -16,12 +16,9 @@ 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; -/** - * Created by MarkPerry on 11/01/2015. - */ public class TreeMapTest { @Test @@ -38,10 +35,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 +63,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); diff --git a/core/src/test/java/fj/data/TreeTest.java b/core/src/test/java/fj/data/TreeTest.java index 811826fb..a6028f37 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.hamcrest.MatcherAssert.assertThat; -/** - * Created by MarkPerry on 29/08/2015. - */ public class TreeTest { @Test 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..c2549963 --- /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.hamcrest.MatcherAssert.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())); + } +} 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/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java new file mode 100644 index 00000000..4505499c --- /dev/null +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -0,0 +1,748 @@ +package fj.data; + +import fj.P; +import fj.P2; +import fj.Semigroup; +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.*; +import static fj.Ord.*; +import static fj.P.*; +import static fj.Semigroup.firstSemigroup; +import static fj.data.Either.left; +import static fj.data.Either.right; +import static fj.data.List.*; +import static fj.data.Option.*; +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.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.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; + + +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 + 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(); + } + + @Test(expected = Error.class) + 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")); + } + + @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()) + .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()))); + }); + } + + @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()) + .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()))); + }); + + } + + @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()) + .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()))); + }); + + } + + @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()) + .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()))); + }); + } + + @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()) + .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()))); + }); + } + + @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()) + .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()))); + }); + } + + @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()) + .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()))); + }); + } + + @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()) + .foreachDoEffect(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate()); + }); + } + + @Test + public void testAccumulate1Complex() { + 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()) + .foreachDoEffect(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate(identity())); + }); + + } + + @Test + public void testAccumulate2Complex() { + 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()) + .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)); + }); + } + + @Test + public void testAccumulate3Complex() { + 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()) + .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)); + }); + + } + + @Test + public void testAccumulate4Complex() { + 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()) + .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)); + }); + + } + + @Test + public void testAccumulate5Complex() { + 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()) + .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)); + }); + } + + @Test + public void testAccumulate6Complex() { + 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()) + .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)); + }); + } + + @Test + public void testAccumulate7Complex() { + 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()) + .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)); + }); + } + + @Test + public void testAccumulate8Complex() { + 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()) + .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)); + }); + } + + @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"))); + 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(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 + 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, 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(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 + 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)))); + } + +} 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..78d837aa --- /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.hamcrest.MatcherAssert.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))); + } +} diff --git a/core/src/test/java/fj/data/hamt/HamtTest.java b/core/src/test/java/fj/data/hamt/HamtTest.java new file mode 100644 index 00000000..ce901e6e --- /dev/null +++ b/core/src/test/java/fj/data/hamt/HamtTest.java @@ -0,0 +1,67 @@ +package fj.data.hamt; + +import fj.Ord; +import fj.P2; +import fj.data.List; +import fj.data.Option; +import org.junit.Test; + +import static fj.Equal.intEqual; +import static fj.Equal.optionEqual; +import static fj.Hash.intHash; +import static fj.P.p; +import static fj.data.List.list; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + + +/** + * @author Mark Perry + */ +public class HamtTest { + + public static final HashArrayMappedTrie empty = HashArrayMappedTrie.emptyKeyInteger(); + + @Test + public void empty() { + assertThat(empty.length(), equalTo(0)); + } + + @Test + public void lengthOne() { + assertThat(empty.set(3, 6).length(), equalTo(1)); + } + + @Test + public void updateLength() { + HashArrayMappedTrie h1 = empty.set(3, 3).set(3, 5); + assertThat(h1.length(), equalTo(1)); + } + + @Test + public void streamLength() { + List> list = list(p(0, 1), p(31, 1), p(32, 1), p(33, 1)); + HashArrayMappedTrie h2 = empty.set(list); + assertThat(h2.toStream().length(), equalTo(list.length())); + } + + @Test + public void allIn() { + List> list = List.list(p(-5, 0), p(-1, -5), p(2, 4), p(4, -2)); + HashArrayMappedTrie h = empty.set(list); + Boolean b = list.foldLeft((acc, p) -> h.find(p._1()).option(false, i -> true && acc), true); + assertThat(b, equalTo(true)); + } + + @Test + public void sampleInts() { + List> ps = List.list(p(-3, 0), p(1, 2)); + int key = -3; + HashArrayMappedTrie h = empty.set(ps); + Option o1 = ps.find(p -> intEqual.eq(p._1(), key)).map(p -> p._2()); + Option o2 = h.find(key); + boolean b = optionEqual(intEqual).eq(o1, o2); + assertThat(b, equalTo(true)); + } + +} 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..f9559d3f --- /dev/null +++ b/core/src/test/java/fj/data/optic/IsoTest.java @@ -0,0 +1,44 @@ +package fj.data.optic; + +import fj.P; +import fj.P2; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.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..98be7fca --- /dev/null +++ b/core/src/test/java/fj/data/optic/LensTest.java @@ -0,0 +1,124 @@ +package fj.data.optic; + +import fj.F; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.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..7fddb31b --- /dev/null +++ b/core/src/test/java/fj/data/optic/OptionalTest.java @@ -0,0 +1,30 @@ +package fj.data.optic; + +import fj.data.Option; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.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..ce6dddb4 --- /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.hamcrest.MatcherAssert.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..9cfc25cc --- /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.hamcrest.MatcherAssert.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)); + } + +} 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..34f8686c --- /dev/null +++ b/core/src/test/java/fj/data/vector/VTest.java @@ -0,0 +1,107 @@ +package fj.data.vector; + +import fj.*; +import fj.data.Array; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.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)); + } + + @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))); + } + +} diff --git a/core/src/test/java/fj/function/DoublesTest.java b/core/src/test/java/fj/function/DoublesTest.java index 6ba98fa2..53b74e52 100644 --- a/core/src/test/java/fj/function/DoublesTest.java +++ b/core/src/test/java/fj/function/DoublesTest.java @@ -1,6 +1,8 @@ package fj.function; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + import fj.F; import org.junit.Test; @@ -9,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/IntegersTest.java b/core/src/test/java/fj/function/IntegersTest.java new file mode 100644 index 00000000..c07197af --- /dev/null +++ b/core/src/test/java/fj/function/IntegersTest.java @@ -0,0 +1,65 @@ +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.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; + +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); + } + } + +} 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..196da20f --- /dev/null +++ b/core/src/test/java/fj/function/LongsTest.java @@ -0,0 +1,65 @@ +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.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +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); + } + } + +} diff --git a/core/src/test/java/fj/function/StringsTest.java b/core/src/test/java/fj/function/StringsTest.java new file mode 100644 index 00000000..db20fd68 --- /dev/null +++ b/core/src/test/java/fj/function/StringsTest.java @@ -0,0 +1,68 @@ +package fj.function; + +import fj.Function; +import org.junit.Test; + +import static fj.Function.compose; +import static fj.function.Strings.*; +import static org.junit.Assert.*; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class StringsTest { + @Test + public void testLines() { + assertThat(compose(unlines(), lines()).f("one two three"), is("one two three")); + } + + @Test + public void testLinesEmpty() { + assertThat(unlines().o(lines()).f(""), is("")); + } + + @Test + public void testLength() { + assertThat(length.f("functionaljava"), is(14)); + } + + @Test + public void testMatches() { + assertThat(matches.f("foo").f("foo"), is(true)); + } + + @Test + public void testContains() { + assertThat(contains.f("bar").f("foobar1"), is(true)); + } + + @Test(expected = NullPointerException.class) + public void testIsEmptyException() { + assertThat(isEmpty.f(null), is(true)); + } + + @Test + public void testIsEmpty() { + assertThat(isEmpty.f(""), is(true)); + } + + @Test + public void testIsNotNullOrEmpty() { + assertThat(isNotNullOrEmpty.f("foo"), is(true)); + } + + @Test + public void testIsNullOrEmpty() { + assertThat(isNullOrEmpty.f(null), is(true)); + } + + @Test + public void testIsNotNullOrBlank() { + assertThat(isNotNullOrBlank.f("foo"), is(true)); + } + + @Test + public void testIsNullOrBlank() { + assertThat(isNullOrBlank.f(" "), is(true)); + } +} 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/core/src/test/java/fj/function/VisitorTest.java b/core/src/test/java/fj/function/VisitorTest.java new file mode 100644 index 00000000..da92a4bb --- /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.hamcrest.MatcherAssert.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")); + } +} 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..f2664a1f --- /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.hamcrest.MatcherAssert.assertThat; + +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); + } + } + +} diff --git a/demo/build.gradle b/demo/build.gradle index 0b83f4f8..87bf7a5c 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -5,10 +5,15 @@ mainClassName = "fj.demo.euler.Problem2" archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() - dependencies { - compile project(":core") - compile project(":quickcheck") - testCompile dependencyJunit + api project(":core") + api project(":quickcheck") + testImplementation junitCompile + testImplementation junitRuntime +} + +test { + jacoco { + enabled = false + } } diff --git a/demo/src/main/java/fj/demo/Array_bind.java b/demo/src/main/java/fj/demo/Array_bind.java index a0182dd1..6048ce62 100644 --- a/demo/src/main/java/fj/demo/Array_bind.java +++ b/demo/src/main/java/fj/demo/Array_bind.java @@ -1,6 +1,5 @@ package fj.demo; -import fj.F; import fj.data.Array; import static fj.data.Array.array; import static fj.Show.arrayShow; diff --git a/demo/src/main/java/fj/demo/Array_foldLeft.java b/demo/src/main/java/fj/demo/Array_foldLeft.java index 9acb86be..f7f1ce58 100644 --- a/demo/src/main/java/fj/demo/Array_foldLeft.java +++ b/demo/src/main/java/fj/demo/Array_foldLeft.java @@ -12,7 +12,7 @@ public static void main(final String[] args) { // WARNING: In JDK 8, update 20 and 25 (current version) the following code triggers an internal JDK compiler error, likely due to https://bugs.openjdk.java.net/browse/JDK-8062253. The code below is a workaround for this compiler bug. // final int c = a.foldLeft(i -> (j -> i + j), 0); - F> add2 = i -> (j -> i + j); + F> add2 = i -> j -> i + j; final int c = a.foldLeft(add2, 0); System.out.println(b); // 1774 } diff --git a/demo/src/main/java/fj/demo/ChequeWrite.java b/demo/src/main/java/fj/demo/ChequeWrite.java index 44ee2956..6e28153c 100644 --- a/demo/src/main/java/fj/demo/ChequeWrite.java +++ b/demo/src/main/java/fj/demo/ChequeWrite.java @@ -1,7 +1,6 @@ package fj.demo; import fj.F; -import fj.F2; import fj.P2; import fj.data.List; import fj.data.Option; @@ -25,7 +24,7 @@ public final class ChequeWrite { private ChequeWrite() {} static List toZero(final int from) { - return unfold(i -> i < 0 ? Option.>none() : some(p(i, i - 1)), from); + return unfold(i -> i < 0 ? Option.none() : some(p(i, i - 1)), from); } static int signum(final int i) { @@ -61,9 +60,9 @@ static List show(final List cs) { : stringShow.showl( list("twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety").index( d1 - '0' - 2)) - .append(d2 == '0' ? List.nil() : show(d2).cons('-')) + .append(d2 == '0' ? List.nil() : show(d2).cons('-')) : d1 == '0' && d2 == '0' && d2r.head() == '0' - ? List.nil() + ? List.nil() : d1 == '0' ? show(list(d2, d2r.head())) : d2 == '0' && d2r.head() == '0' @@ -77,7 +76,7 @@ static List, Integer>> split(final List as) { final int len = as.length(); final List> ds = as.zip(toZero(len - 1)).foldRight((ki, z) -> - ki._2() % 3 == 0 ? z.conss(single(ki._1())) : z.tail().conss(z.head().cons(ki._1())), List.>nil() + ki._2() % 3 == 0 ? z.conss(single(ki._1())) : z.tail().conss(z.head().cons(ki._1())), List.nil() ); return ds.zip(toZero(len / 3 + signum(len % 3) - 1)); } @@ -107,24 +106,22 @@ static boolean eq(final List a, final List b) { if (cs.isEmpty()) return fromString("zero dollars"); else { - final List.Buffer> x = new List.Buffer>(); + final List.Buffer> x = new List.Buffer<>(); final List, Integer>> k = split(cs); final int c = k.head()._2(); - k.foreachDoEffect(new Effect1, Integer>>() { - public void f(final P2, Integer> z) { - final List w = z._1(); - final int i = z._2(); + k.foreachDoEffect(z -> { + final List w = z._1(); + final int i = z._2(); - if (i == 0 && c > 0 && and(w)) - x.snoc(fromString("and")); + if (i == 0 && c > 0 && and(w)) + x.snoc(fromString("and")); - if (existsNotZero(w)) { - x.snoc(show(w)); - if (i != 0) - x.snoc(illion(i - 1)); - } + if (existsNotZero(w)) { + x.snoc(show(w)); + if (i != 0) + x.snoc(illion(i - 1)); } }); diff --git a/demo/src/main/java/fj/demo/Comonad_example.java b/demo/src/main/java/fj/demo/Comonad_example.java index 70230e1d..3b5661f8 100644 --- a/demo/src/main/java/fj/demo/Comonad_example.java +++ b/demo/src/main/java/fj/demo/Comonad_example.java @@ -1,7 +1,5 @@ package fj.demo; -import fj.F; -import fj.F1Functions; import fj.P; import static fj.data.List.asString; import static fj.data.List.fromString; @@ -23,11 +21,11 @@ public static void main(final String[] args) { } public static Stream> perms(final Stream s) { - Stream> r = single(Stream.nil()); + Stream> r = single(Stream.nil()); 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/HList_foldRight.java b/demo/src/main/java/fj/demo/HList_foldRight.java index 4f5ee3e1..51f6f4c2 100644 --- a/demo/src/main/java/fj/demo/HList_foldRight.java +++ b/demo/src/main/java/fj/demo/HList_foldRight.java @@ -4,8 +4,6 @@ import static fj.Function.identity; import fj.P2; import fj.Unit; -import static fj.Unit.unit; -import static java.lang.System.out; import fj.data.hlist.HList; import static fj.data.hlist.HList.single; diff --git a/demo/src/main/java/fj/demo/IODemo.java b/demo/src/main/java/fj/demo/IODemo.java index 316eafc4..aad8e41b 100644 --- a/demo/src/main/java/fj/demo/IODemo.java +++ b/demo/src/main/java/fj/demo/IODemo.java @@ -4,16 +4,12 @@ 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_; 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) { @@ -28,16 +24,16 @@ public static void main(String[] args) { * Reads from standard input until the line length is less than three * and prints that last line. */ - public void readFirstShortLine() { - F f = lift(lines_()).andThen(l -> l.filter(s -> s.length() < 3)).andThen(unlines_()); + public final void readFirstShortLine() { + F f = lines_().andThen(l -> l.filter(s -> s.length() < 3)).andThen(unlines_()); runSafe(interact(f)); } /** * Read a stream of input lazily using interact, in effect reading the first line */ - public void readFirstLine() { - F f = lift((LazyString s) -> s.lines()).andThen(unlines_()); + public final void readFirstLine() { + F f = lines_().andThen(unlines_()); runSafe(interact(f)); } @@ -45,14 +41,14 @@ public void readFirstLine() { * Demonstrate use of interact, just echoing the lazy string. Reading lines is done * lazily, so just the first line is read. */ - public void simpleInteract() { + public final void simpleInteract() { runSafe(interact(s -> s)); } /** * Demonstrate that getContents returns a lazy string. */ - public void getContents() { + public final void getContents() { out.println(runSafe(IOFunctions.getContents())); } diff --git a/demo/src/main/java/fj/demo/IOWalkthrough.java b/demo/src/main/java/fj/demo/IOWalkthrough.java new file mode 100644 index 00000000..2f1cb9eb --- /dev/null +++ b/demo/src/main/java/fj/demo/IOWalkthrough.java @@ -0,0 +1,81 @@ +package fj.demo; + +import fj.F; +import fj.Unit; +import fj.data.IO; +import fj.data.IOFunctions; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import static fj.data.IOFunctions.stdinReadLine; +import static fj.data.IOFunctions.stdoutPrint; +import static fj.data.IOFunctions.stdoutPrintln; +import static fj.data.IOFunctions.toSafeValidation; + +/** + * Demonstrates how to use IO and basic function composition. + */ +public class IOWalkthrough { + public static void main(String[] args) { + + // IO is just a container to defer a computation (lazy), with the intention to encapsulate computations that either + // consume and/or produce side-effects + // the computation is not (yet) executed on creation hence it can be treated like a value + + final IO askName = () -> { + System.out.println("Hi, what's your name?"); + return Unit.unit(); + }; + + // fj.data.IOFunctions contains a lot of convenience functions regarding IO, the above example could be rewritten with IOFunctions.stdoutPrintln + // we now create an IO value to prompt for the name if executed + + IO promptName = IOFunctions.stdoutPrint("Name: "); + + // we can compose these two values with fj.data.IOFunctions.append, since they both are not interested in any runtime value + + IO askAndPromptName = IOFunctions.append(askName, promptName); + + // now we create an IO value to read a line from stdin + + final IO readName = () -> new BufferedReader(new InputStreamReader(System.in)).readLine(); + + // this is the same as IOFunctions.stdinReadLine() + + // 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 = 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 + + final IO readAndPrintUpperCasedName = IOFunctions.bind(readName, upperCaseAndPrint); + + // so append is really just a specialised form of bind, ignoring the runtime value of the IO execution that was composed before us + + final IO program = IOFunctions.bind(askAndPromptName, ignored -> readAndPrintUpperCasedName); + + // this is the same as writing IOFunctions.append(askAndPromptName, readAndPrintUpperCasedName) + + // we have recorded the entire program, but have not run anything yet + // now we get to the small dirty part at the end of our program where we actually execute it + // we can either choose to just call program.run(), which allows the execution to escape + // or we use safe to receive an fj.data.Either with the potential exception on the left side + + toSafeValidation(program).run().on((IOException e) -> { e.printStackTrace(); return Unit.unit(); }); + + // doing function composition like this can be quite cumbersome, since you will end up nesting parenthesis unless you flatten it out by + // 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; + stdoutPrintln("What's your name again?") + .append(stdoutPrint("Name: ")) + .append(stdinReadLine()) + .bind(f.andThen(IOFunctions::stdoutPrintln)) + .safe().run().on((IOException e) -> { e.printStackTrace(); return Unit.unit(); }); + } +} + diff --git a/demo/src/main/java/fj/demo/List_groupBy.java b/demo/src/main/java/fj/demo/List_groupBy.java index 1dc8866d..b415e19c 100644 --- a/demo/src/main/java/fj/demo/List_groupBy.java +++ b/demo/src/main/java/fj/demo/List_groupBy.java @@ -18,31 +18,25 @@ public static void main(final String... args) { private static void keyDemo() { System.out.println("KeyDemo"); final List words = list("Hello", "World", "how", "are", "your", "doing"); - final TreeMap> lengthMap = words.groupBy(String::length); + final TreeMap> lengthMap = words.groupBy(String::length, Ord.intOrd); - lengthMap.forEach(entry -> { - System.out.println(String.format("Words with %d chars: %s", entry._1(), entry._2())); - }); + lengthMap.forEach(entry -> System.out.println(String.format("Words with %d chars: %s", entry._1(), entry._2()))); } private static void keyValueDemo() { System.out.println("KeyValueDemo"); final List xs = list(1, 2, 3, 4, 5, 6, 7, 8, 9); - final TreeMap> result = xs.groupBy(x -> x % 3, Integer::toBinaryString); + final TreeMap> result = xs.groupBy(x -> x % 3, Integer::toBinaryString, Ord.intOrd); - result.forEach(entry -> { - System.out.println(String.format("Numbers with reminder %d are %s", entry._1(), entry._2())); - }); + result.forEach(entry -> System.out.println(String.format("Numbers with reminder %d are %s", entry._1(), entry._2()))); } private static void keyValueAccDemo() { System.out.println("KeyValueAccDemo"); final List words = list("Hello", "World", "how", "are", "your", "doing"); final TreeMap lengthCounts = - words.groupBy(String::length, Function.identity(), 0, (word, sum) -> sum + 1, Ord.hashOrd()); + words.groupBy(String::length, Function.identity(), 0, (word, sum) -> sum + 1, Ord.intOrd); - lengthCounts.forEach(entry -> { - System.out.println(String.format("Words with %d chars: %s", entry._1(), entry._2())); - }); + lengthCounts.forEach(entry -> System.out.println(String.format("Words with %d chars: %s", entry._1(), entry._2()))); } } diff --git a/demo/src/main/java/fj/demo/List_map.java b/demo/src/main/java/fj/demo/List_map.java index 55216810..a07f0015 100644 --- a/demo/src/main/java/fj/demo/List_map.java +++ b/demo/src/main/java/fj/demo/List_map.java @@ -9,8 +9,8 @@ public final class List_map { public static void main(final String[] args) { final List a = list(1, 2, 3); - final List b = a.map(add.f(42)); - final List c = a.map(i -> i = 42); + final List b = a.map(add.f(42)); // or equivalently: + final List c = a.map(i -> i + 42); listShow(intShow).println(b); // [43,44,45] } } diff --git a/demo/src/main/java/fj/demo/Option_bind.java b/demo/src/main/java/fj/demo/Option_bind.java index d02a1493..edf3dd83 100644 --- a/demo/src/main/java/fj/demo/Option_bind.java +++ b/demo/src/main/java/fj/demo/Option_bind.java @@ -18,9 +18,9 @@ public static void main(final String[] args) { final Option o5 = o2.bind(f); final Option o6 = o3.bind(f); - final Option p1 = o1.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); - final Option p2 = o2.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); - final Option p3 = o3.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); + final Option p1 = o1.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); + final Option p2 = o2.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); + final Option p3 = o3.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); optionShow(intShow).println(o4); // None optionShow(intShow).println(o5); // Some(24) diff --git a/demo/src/main/java/fj/demo/Primes2.java b/demo/src/main/java/fj/demo/Primes2.java index 4a5b9dbd..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 fj.P1; 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/Set_map.java b/demo/src/main/java/fj/demo/Set_map.java index ff5428df..054fbc15 100644 --- a/demo/src/main/java/fj/demo/Set_map.java +++ b/demo/src/main/java/fj/demo/Set_map.java @@ -1,6 +1,5 @@ package fj.demo; -import fj.F; import fj.data.Set; import static fj.data.Set.empty; import fj.Ord; 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 0f09318a..75de4823 100644 --- a/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java +++ b/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java @@ -1,18 +1,14 @@ package fj.demo; -import fj.F2; import fj.data.List; import fj.data.State; 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 }; + public enum Input { COIN, TURN } public static class VendingMachine { @@ -30,7 +26,7 @@ public VendingMachine(boolean lock, int things, int numCoins) { * Equals generated by Intellij */ @Override - public boolean equals(Object o) { + public final boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -47,18 +43,18 @@ public boolean equals(Object o) { * HashCode generated by Intellij */ @Override - public int hashCode() { - int result = (locked ? 1 : 0); + public final int hashCode() { + int result = locked ? 1 : 0; result = 31 * result + items; result = 31 * result + coins; return result; } - public String toString() { + public final String toString() { return String.format("VendingMachine(locked=%b,items=%d,coins=%d)", locked, items, coins); } - VendingMachine next(Input i) { + final VendingMachine next(Input i) { if (items == 0) { return this; } else if (i == COIN && !locked) { @@ -76,7 +72,7 @@ VendingMachine next(Input i) { } static State simulate(List list) { - return list.foldLeft((s, i) -> s.map(m -> m.next(i)), State.init()); + return list.foldLeft((s, i) -> s.map(m -> m.next(i)), State.init()); } static void test() { diff --git a/demo/src/main/java/fj/demo/WriterDemo_Halver.java b/demo/src/main/java/fj/demo/WriterDemo_Halver.java index a2a270f6..9e6c6dac 100644 --- a/demo/src/main/java/fj/demo/WriterDemo_Halver.java +++ b/demo/src/main/java/fj/demo/WriterDemo_Halver.java @@ -4,12 +4,8 @@ import fj.P2; import fj.data.Writer; -import static fj.F1Functions.map; import static fj.Monoid.stringMonoid; -/** - * Created by mperry on 4/08/2014. - */ public class WriterDemo_Halver { public static void main(String args[]) { @@ -24,7 +20,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/Fibs.java b/demo/src/main/java/fj/demo/concurrent/Fibs.java index b3aae799..e2e94fc8 100644 --- a/demo/src/main/java/fj/demo/concurrent/Fibs.java +++ b/demo/src/main/java/fj/demo/concurrent/Fibs.java @@ -1,7 +1,7 @@ package fj.demo.concurrent; import static fj.Bottom.error; -import fj.Effect; + import fj.F; import fj.P; import fj.P2; @@ -52,7 +52,7 @@ public static void main(final String[] args) {return n < CUTOFF ? promise(su, P.p(seqFib(n))) : f(n - 1).bind(f(n - 2), add);}}; System.out.println("Calculating Fibonacci sequence in parallel..."); - join(su, spi.parMap(fib, range(0, 46)).map(Promise.sequence(su))).to(out);} + join(su, spi.parMap(fib, range(0, 46)).map(Promise.sequence(su))).to(out);} // The sequential version of the recursive Fibonacci function public static int seqFib(final int n) diff --git a/demo/src/main/java/fj/demo/concurrent/MapReduce.java b/demo/src/main/java/fj/demo/concurrent/MapReduce.java index 57a59d9e..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 { @@ -64,7 +65,7 @@ public Stream f(final BufferedReader reader) { })); final ExecutorService pool = newFixedThreadPool(16); - final ParModule m = parModule(Strategy.executorStrategy(pool)); + final ParModule m = parModule(Strategy.executorStrategy(pool)); System.out.println("Word Count: " + countWords(documents, m).claim()); diff --git a/demo/src/main/java/fj/demo/concurrent/Ping.java b/demo/src/main/java/fj/demo/concurrent/Ping.java index 82aad6b3..da2efd52 100644 --- a/demo/src/main/java/fj/demo/concurrent/Ping.java +++ b/demo/src/main/java/fj/demo/concurrent/Ping.java @@ -1,6 +1,5 @@ package fj.demo.concurrent; -import fj.Effect; import fj.Unit; import fj.P1; import fj.control.parallel.Actor; @@ -34,12 +33,12 @@ public void f(final Pong pong) { } // Commence pinging - public P1 start() { + public final P1 start() { return pong.act(this); } // Receive a pong - public P1 act(final Pong p) { + public final P1 act(final Pong p) { return ping.act(p); } } diff --git a/demo/src/main/java/fj/demo/concurrent/PingPong.java b/demo/src/main/java/fj/demo/concurrent/PingPong.java index 59038679..d845ab32 100644 --- a/demo/src/main/java/fj/demo/concurrent/PingPong.java +++ b/demo/src/main/java/fj/demo/concurrent/PingPong.java @@ -2,7 +2,6 @@ import static fj.Bottom.error; import fj.Unit; -import fj.Effect; import fj.control.parallel.Strategy; import fj.control.parallel.Actor; import fj.function.Effect1; @@ -16,7 +15,7 @@ * Example of parallel synchronous messaging using Actors in Functional Java. * Author: Runar */ -@SuppressWarnings({"ArithmeticOnVolatileField"}) +@SuppressWarnings("ArithmeticOnVolatileField") public class PingPong { private final int actors; private final int pings; @@ -31,15 +30,13 @@ public PingPong(final ExecutorService pool, final int actors, final int pings) { // This actor gives feedback to the user that work is being done // and also terminates the program when all work has been completed. - callback = Actor.queueActor(s, new Effect1() { - public void f(final Integer i) { - done++; - if (done >= actors) { - System.out.println("All done."); - pool.shutdown(); - } else if (actors < 10 || done % (actors / 10) == 0) - System.out.println(MessageFormat.format("{0} actors done ({1} total pongs).", done, pings * done)); - } + callback = Actor.queueActor(s, i -> { + done++; + if (done >= actors) { + System.out.println("All done."); + pool.shutdown(); + } else if (actors < 10 || done % (actors / 10) == 0) + System.out.println(MessageFormat.format("{0} actors done ({1} total pongs).", done, pings * done)); }); } @@ -54,7 +51,7 @@ public static void main(final String[] args) { new PingPong(Executors.newFixedThreadPool(threads), actors, pings).start(); } - public void start() { + public final void start() { // We will use one Pong actor... final Pong pong = new Pong(s); diff --git a/demo/src/main/java/fj/demo/concurrent/Pong.java b/demo/src/main/java/fj/demo/concurrent/Pong.java index d85eda7e..bc06a22d 100644 --- a/demo/src/main/java/fj/demo/concurrent/Pong.java +++ b/demo/src/main/java/fj/demo/concurrent/Pong.java @@ -1,6 +1,5 @@ package fj.demo.concurrent; -import fj.Effect; import fj.Unit; import fj.P1; import fj.control.parallel.Actor; @@ -24,7 +23,7 @@ public void f(final Ping m) { } // Receive a ping - public P1 act(final Ping ping) { + public final P1 act(final Ping ping) { return p.act(ping); } } \ No newline at end of file diff --git a/demo/src/main/java/fj/demo/concurrent/WordCount.java b/demo/src/main/java/fj/demo/concurrent/WordCount.java index 67fef136..175d2d09 100644 --- a/demo/src/main/java/fj/demo/concurrent/WordCount.java +++ b/demo/src/main/java/fj/demo/concurrent/WordCount.java @@ -1,9 +1,18 @@ package fj.demo.concurrent; -import static fj.Monoid.monoid; -import static fj.control.parallel.ParModule.parModule; -import static fj.data.List.nil; -import static java.util.concurrent.Executors.newFixedThreadPool; +import fj.F; +import fj.Monoid; +import fj.P; +import fj.P1; +import fj.P2; +import fj.control.parallel.ParModule; +import fj.control.parallel.Promise; +import fj.control.parallel.Strategy; +import fj.data.IOFunctions; +import fj.data.Iteratee.Input; +import fj.data.Iteratee.IterV; +import fj.data.List; +import fj.data.Option; import java.io.BufferedWriter; import java.io.File; @@ -13,35 +22,25 @@ import java.io.Reader; import java.io.StringWriter; import java.io.Writer; -import java.nio.charset.Charset; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; -import fj.F; -import fj.F2; -import fj.Monoid; -import fj.P; -import fj.P1; -import fj.P2; -import fj.Unit; -import fj.control.parallel.ParModule; -import fj.control.parallel.Promise; -import fj.control.parallel.Strategy; -import fj.data.*; -import fj.data.Iteratee.Input; -import fj.data.Iteratee.IterV; -import fj.function.Effect1; +import static fj.Monoid.monoid; +import static fj.control.parallel.ParModule.parModule; +import static fj.data.List.nil; +import static java.util.concurrent.Executors.newFixedThreadPool; /** * Reads words and their counts from files ({@link #getWordsAndCountsFromFiles} in a single thread * and {@link #getWordsAndCountsFromFilesInParallel} in multiple threads). The files are created * initially and populated with some sample content. - * + * * @author Martin Grotzke */ public class WordCount { - + // Integers.add.f(1) caused an SOE... private static final F addOne = a -> a.intValue() + 1; @@ -54,26 +53,26 @@ private static Map update(Map map, K key, F valueFuncti map.put(key, valueFunction.f(value)); return map; } - + private static final F> fileNameToWordsAndCountsWithCharChunkIteratee = fileName -> { try { - return IOFunctions.enumFileCharChunks(new File(fileName), Option.none(), wordCountsFromCharChunks()).run().run(); + return IOFunctions.enumFileCharChunks(new File(fileName), Option.none(), wordCountsFromCharChunks()).run().run(); } catch (final IOException e) { throw new RuntimeException(e); } }; - + private static final F> fileNameToWordsAndCountsWithCharChunk2Iteratee = fileName -> { try { - return IOFunctions.enumFileChars(new File(fileName), Option. none(), wordCountsFromChars()).run().run(); + return IOFunctions.enumFileChars(new File(fileName), Option.none(), wordCountsFromChars()).run().run(); } catch (final IOException e) { throw new RuntimeException(e); } }; - + private static final F> fileNameToWordsAndCountsWithCharIteratee = fileName -> { try { - return IOFunctions.enumFileChars(new File(fileName), Option. none(), wordCountsFromChars()).run().run(); + return IOFunctions.enumFileChars(new File(fileName), Option.none(), wordCountsFromChars()).run().run(); } catch (final IOException e) { throw new RuntimeException(e); } @@ -93,24 +92,21 @@ public F, IterV>> f(final P2>>>() { @Override public F>> _1() { - return new F>>() { - @Override - public IterV> f(final char[] e) { - StringBuilder sb = acc._1(); - Map map = acc._2(); - for(char c : e) { - if(Character.isWhitespace(c)) { - if(sb.length() > 0) { - map = update(map, sb.toString(), addOne, Integer.valueOf(0)); - sb = new StringBuilder(); - } - } - else { - sb.append(c); + return e -> { + StringBuilder sb = acc._1(); + Map map = acc._2(); + for(char c : e) { + if(Character.isWhitespace(c)) { + if(sb.length() > 0) { + map = update(map, sb.toString(), addOne, Integer.valueOf(0)); + sb = new StringBuilder(); } } - return IterV.cont(step.f(P.p(sb, map))); + else { + sb.append(c); + } } + return IterV.cont(step.f(P.p(sb, map))); }; } }; @@ -119,9 +115,9 @@ public IterV> f(final char[] e) { final StringBuilder sb = acc._1(); if(sb.length() > 0) { final Map map = update(acc._2(), sb.toString(), addOne, Integer.valueOf(0)); - return IterV.done(map, Input.eof()); + return IterV.done(map, Input.eof()); } - return IterV.done(acc._2(), Input.eof()); + return IterV.done(acc._2(), Input.eof()); }); return s -> s.apply(empty, el, eof); @@ -140,32 +136,30 @@ public static final IterV> wordCountsFromCha public F, IterV>> f(final P2> acc) { final P1>> empty = P.lazy(() -> IterV.cont(step.f(acc))); final P1>>> el = - P.lazy(() -> { - return e -> { - if(Character.isWhitespace(e.charValue())) { - final StringBuilder sb = acc._1(); - if(sb.length() > 0) { - final Map map = update(acc._2(), sb.toString(), addOne, Integer.valueOf(0)); - return IterV.cont(step.f(P.p(new StringBuilder(), map))); - } - else { - // another whitespace char, no word to push to the map - return IterV.cont(step.f(acc)); - } - } - else { - acc._1().append(e); - return IterV.cont(step.f(acc)); - } - }; - }); + P.lazy(() -> e -> { + if(Character.isWhitespace(e.charValue())) { + final StringBuilder sb = acc._1(); + if(sb.length() > 0) { + final Map map = update(acc._2(), sb.toString(), addOne, Integer.valueOf(0)); + return IterV.cont(step.f(P.p(new StringBuilder(), map))); + } + else { + // another whitespace char, no word to push to the map + return IterV.cont(step.f(acc)); + } + } + else { + acc._1().append(e); + return IterV.cont(step.f(acc)); + } + }); final P1>> eof = P.lazy(() -> { final StringBuilder sb = acc._1(); if(sb.length() > 0) { final Map map = update(acc._2(), sb.toString(), addOne, Integer.valueOf(0)); - return IterV.done(map, Input.eof()); + return IterV.done(map, Input.eof()); } - return IterV.done(acc._2(), Input.eof()); + return IterV.done(acc._2(), Input.eof()); } ); return s -> s.apply(empty, el, eof); @@ -185,7 +179,7 @@ public static void main(String[] args) throws IOException { final Map expectedWordsAndCounts = result._2(); long avgSize = fileNames.foldLeft((a, file) -> a.longValue() + new File(file).length(), 0l) / fileNames.length(); System.out.println("Processing " + numFiles + " files with ~"+numSharedWords+" words and an avg size of " + avgSize + " bytes."); - + // warmup for(int i = 0; i < 1; i++) { // getWordsAndCountsFromFiles(fileNames.take(1)).size(); @@ -215,7 +209,7 @@ public static void main(String[] args) throws IOException { assertTrue(wordsAndCountsFromFiles != null); assertEquals(wordsAndCountsFromFiles.size(), numFiles + numSharedWords); assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); - + System.gc(); // get word counts sequentially / single threaded \w iteratee @@ -227,7 +221,7 @@ public static void main(String[] args) throws IOException { assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); System.gc(); - + // get word counts sequentially / single threaded \w iteratee start = System.currentTimeMillis(); wordsAndCountsFromFiles = getWordsAndCountsFromFilesWithIteratee(fileNames, fileNameToWordsAndCountsWithCharChunk2Iteratee); @@ -237,7 +231,7 @@ public static void main(String[] args) throws IOException { assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); System.gc(); - + // start = System.currentTimeMillis(); // wordsAndCountsFromFiles = getWordsAndCountsFromFilesInParallel(fileNames, fileNameToWordsAndCounts, 8); // System.out.println("Getting word counts in 8 threads took " + (System.currentTimeMillis() - start) + " ms."); @@ -253,21 +247,16 @@ public static void main(String[] args) throws IOException { assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); System.gc(); - + start = System.currentTimeMillis(); wordsAndCountsFromFiles = getWordsAndCountsFromFilesInParallel(fileNames, fileNameToWordsAndCountsWithCharChunkIteratee, 32); System.out.println("Getting word counts in 32 threads with char chunk iteratee took " + (System.currentTimeMillis() - start) + " ms."); assertTrue(wordsAndCountsFromFiles != null); assertEquals(wordsAndCountsFromFiles.size(), numFiles + numSharedWords); assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); - + // we have tmpfiles, but still want to be sure not to leave rubbish - fileNames.foreachDoEffect(new Effect1() { - @Override - public void f(final String a) { - new File(a).delete(); - } - }); + fileNames.foreachDoEffect(a -> new File(a).delete()); } @SuppressWarnings("unused") @@ -279,7 +268,7 @@ private static void print(Map wordsAndCountsFromFiles) { private static P2, Map> writeSampleFiles( int numFiles, int numSharedWords) throws IOException { - final Map expectedWordsAndCounts = new HashMap(); + final Map expectedWordsAndCounts = new HashMap<>(); List fileNames = nil(); for(int i = 0; i < numFiles; i++) { final File file = File.createTempFile("wordcount-"+ i + "-", ".txt"); @@ -295,17 +284,17 @@ private static P2, Map> writeSampleFiles( } return P.p(fileNames, expectedWordsAndCounts); } - + public static Map getWordsAndCountsFromFilesWithIteratee(final List fileNames, final F> fileNameToWordsAndCountsWithIteratee) { final List> maps = fileNames.map(fileNameToWordsAndCountsWithIteratee); - return maps.foldLeft((Map a, Map b) -> plus(a, b), new HashMap()); + return maps.foldLeft(WordCount::plus, new HashMap()); } - + public static Map getWordsAndCountsFromFilesInParallel( final List fileNames, final F> fileNameToWordsAndCounts, int numThreads) { final ExecutorService pool = newFixedThreadPool(numThreads); - final ParModule m = parModule(Strategy. executorStrategy(pool)); + final ParModule m = parModule(Strategy.executorStrategy(pool)); // Long wordCount = countWords(fileNames.map(readFile), m).claim(); final Map result = getWordsAndCountsFromFiles(fileNames, fileNameToWordsAndCounts, m).claim(); @@ -314,26 +303,23 @@ public static Map getWordsAndCountsFromFilesInParallel( return result; } - + // Read documents and extract words and word counts of documents public static Promise> getWordsAndCountsFromFiles( final List fileNames, final F> fileNameToWordsAndCounts, final ParModule m) { - final F, F, Map>> MapSum = - a -> b -> plus(a, b); - final Monoid> monoid = monoid(MapSum, - new HashMap()); + final Monoid> monoid = monoid(WordCount::plus, Collections.emptyMap()); return m.parFoldMap(fileNames, fileNameToWordsAndCounts, monoid); } - + private static Map plus(Map a, Map b) { - final Map result = new HashMap(a); + final Map result = new HashMap<>(a); for(Map.Entry entry : b.entrySet()) { final Integer num = result.get(entry.getKey()); result.put(entry.getKey(), num != null ? num.intValue() + entry.getValue() : entry.getValue()); } return result; } - + @SuppressWarnings("unused") private static String readFileToString(File file) throws IOException { Reader reader = null; @@ -354,13 +340,13 @@ private static void copy(Reader reader, Writer writer) throws IOException { writer.write(buffer, 0, n); } } - + static void assertTrue(boolean condition) { if (!condition) { throw new AssertionError(); } } - + static void assertEquals(Object actual, Object expected) { if (!expected.equals(actual)) { throw new IllegalArgumentException("Not equals. Expected: " + expected + ", actual: " + actual); diff --git a/demo/src/main/java/fj/demo/euler/Problem1.java b/demo/src/main/java/fj/demo/euler/Problem1.java index 82dc53c0..e20f9a97 100644 --- a/demo/src/main/java/fj/demo/euler/Problem1.java +++ b/demo/src/main/java/fj/demo/euler/Problem1.java @@ -1,6 +1,5 @@ package fj.demo.euler; -import fj.F; import fj.data.Stream; import static fj.data.List.range; diff --git a/demo/src/main/java/fj/demo/euler/Problem2.java b/demo/src/main/java/fj/demo/euler/Problem2.java index 321a34b7..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,16 +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) -> { - - return 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/demo/src/main/java/fj/demo/euler/Problem3.java b/demo/src/main/java/fj/demo/euler/Problem3.java index bde87126..e429e0e9 100644 --- a/demo/src/main/java/fj/demo/euler/Problem3.java +++ b/demo/src/main/java/fj/demo/euler/Problem3.java @@ -1,7 +1,5 @@ package fj.demo.euler; -import fj.F; -import fj.P; import fj.P1; import static fj.data.Enumerator.naturalEnumerator; import fj.data.Natural; diff --git a/demo/src/main/java/fj/demo/euler/Problem4.java b/demo/src/main/java/fj/demo/euler/Problem4.java index f1947057..c768ed78 100644 --- a/demo/src/main/java/fj/demo/euler/Problem4.java +++ b/demo/src/main/java/fj/demo/euler/Problem4.java @@ -1,6 +1,5 @@ package fj.demo.euler; -import fj.F; import static fj.Function.flip; import fj.data.Stream; import static fj.data.Stream.iterate; @@ -20,6 +19,6 @@ public static void main(final String[] a) { intShow.println(tdl.tails().bind(tdl.zipWith(multiply)).filter(i -> { final Stream s = intShow.show(i); return streamEqual(charEqual).eq(s.reverse().take(3), s.take(3)); - }).foldLeft1(intOrd.max)); + }).foldLeft1(intOrd::max)); } } diff --git a/demo/src/main/java/fj/demo/optic/LensPerson.java b/demo/src/main/java/fj/demo/optic/LensPerson.java index 107ef659..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 { @@ -48,14 +45,14 @@ public Address(int number, String street) { static final Person oldPerson = new Person(oldName, oldAddress); @Test - public void get() { + public final void get() { assertTrue(personNameLens.get(oldPerson).equals(oldName)); assertTrue(personNumberLens.get(oldPerson) == oldNumber); assertTrue(personStreetLens.get(oldPerson) == oldStreet); } @Test - public void setName() { + public final void setName() { String newName = "Bill"; Person p = personNameLens.set(newName).f(oldPerson); assertTrue(p.name.equals(newName)); @@ -63,7 +60,7 @@ public void setName() { } @Test - public void setNumber() { + public final void setNumber() { int newNumber = 20; Person p = personNumberLens.set(newNumber).f(oldPerson); assertTrue(p.name.equals(oldName)); @@ -72,7 +69,7 @@ public void setNumber() { } @Test - public void setStreet() { + public final void setStreet() { String newStreet = "First St"; Person p = personStreetLens.set(newStreet).f(oldPerson); assertTrue(p.name.equals(oldName)); diff --git a/demo/src/main/java/fj/demo/realworld/Chapter7.java b/demo/src/main/java/fj/demo/realworld/Chapter7.java index 2f6ed8a4..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. @@ -28,7 +26,7 @@ public static void main(String[] args) { public static void toUpperLazy() { runSafe(interact(ls -> { Stream stream = ls.lines().map((LazyString ls2) -> ls2.eval().toUpperCase()); - return LazyString.unlines(stream.map(s -> LazyString.str(s))); + return LazyString.unlines(stream.map(LazyString::str)); })); } @@ -36,7 +34,7 @@ public static void toUpperLazy() { * Read each line, convert to uppercase and print on stdout, until an empty line */ public static void toUpperByLine() { - Stream> s1 = Stream.repeat(IOFunctions.stdinReadLine()); + Stream> s1 = Stream.repeat(stdinReadLine()); IO> io = sequenceWhile(s1, s -> s.trim().length() > 0); runSafe(io).foreachDoEffect(s -> runSafe(stdoutPrintln(s.toUpperCase()))); } @@ -46,7 +44,7 @@ public static void toUpperByLine() { * uppercase line to stdout */ public static void toUpperInteract() { - runSafe(interactWhile(s -> s.trim().length() > 0, s -> s.toUpperCase())); + runSafe(interactWhile(s -> s.trim().length() > 0, String::toUpperCase)); } } diff --git a/demo/src/main/java/fj/demo/test/AdditionCommutes.java b/demo/src/main/java/fj/demo/test/AdditionCommutes.java index 8aea4f03..7b2a6571 100644 --- a/demo/src/main/java/fj/demo/test/AdditionCommutes.java +++ b/demo/src/main/java/fj/demo/test/AdditionCommutes.java @@ -1,6 +1,5 @@ package fj.demo.test; -import fj.F2; import static fj.test.Arbitrary.arbInteger; import static fj.test.CheckResult.summary; import fj.test.Property; diff --git a/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java b/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java index b1629c21..d61ffeb0 100644 --- a/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java +++ b/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java @@ -1,8 +1,6 @@ package fj.demo.test; -import fj.F2; import fj.P; -import fj.P1; import fj.control.parallel.Strategy; import static fj.test.Arbitrary.arbInteger; import static fj.test.CheckResult.summary; diff --git a/demo/src/main/java/fj/demo/test/EqualsHashCode.java b/demo/src/main/java/fj/demo/test/EqualsHashCode.java index 20a732a2..a9f0ef2c 100644 --- a/demo/src/main/java/fj/demo/test/EqualsHashCode.java +++ b/demo/src/main/java/fj/demo/test/EqualsHashCode.java @@ -1,13 +1,9 @@ package fj.demo.test; -import fj.F; -import fj.F2; -import fj.F3; import static fj.Function.curry; -import fj.test.Arbitrary; +import fj.test.Gen; import static fj.test.Arbitrary.arbByte; import static fj.test.Arbitrary.arbCharacter; -import static fj.test.Arbitrary.arbitrary; import static fj.test.Bool.bool; import static fj.test.CheckResult.summary; import fj.test.Property; @@ -58,14 +54,14 @@ public int hashCode() { public static void main(final String[] args) { // Restrictive arbitrary for Byte, produces from three possible values. - final Arbitrary arbByteR = arbitrary(arbByte.gen.map(b -> (byte)(b % 3))); + final Gen arbByteR = arbByte.map(b -> (byte)(b % 3)); // Restrictive arbitrary for String, produces from twelve (2 * 3 * 2) possible values. - final Arbitrary arbStringR = arbitrary(arbCharacter.gen.bind(arbCharacter.gen, arbCharacter.gen, curry((c1, c2, c3) -> new String(new char[]{(char)(c1 % 2 + 'a'), (char)(c2 % 3 + 'a'), (char)(c3 % 2 + 'a')})))); + final Gen arbStringR = arbCharacter.bind(arbCharacter, arbCharacter, curry((c1, c2, c3) -> new String(new char[]{(char)(c1 % 2 + 'a'), (char)(c2 % 3 + 'a'), (char)(c3 % 2 + 'a')}))); // Arbitrary for MyClass that uses the restrictive arbitraries above. // We are using the monad pattern (bind) to make this a trivial exercise. - final Arbitrary arbMyClass = arbitrary(arbByteR.gen.bind(arbStringR.gen, curry((b, s) -> new MyClass(b, s)))); + final Gen arbMyClass = arbByteR.bind(arbStringR, curry(MyClass::new)); // Finally the property. // if m1 equals m2, then this implies that m1's hashCode is equal to m2's hashCode. diff --git a/demo/src/main/java/fj/demo/test/IntegerOverflow.java b/demo/src/main/java/fj/demo/test/IntegerOverflow.java index 155f8495..48b9d2dc 100644 --- a/demo/src/main/java/fj/demo/test/IntegerOverflow.java +++ b/demo/src/main/java/fj/demo/test/IntegerOverflow.java @@ -1,6 +1,5 @@ package fj.demo.test; -import fj.F2; import static fj.test.Arbitrary.arbIntegerBoundaries; import static fj.test.Bool.bool; import static fj.test.CheckResult.summary; diff --git a/demo/src/main/java/fj/demo/test/JavaLinkedList.java b/demo/src/main/java/fj/demo/test/JavaLinkedList.java index 0954c705..ba8ec1f1 100644 --- a/demo/src/main/java/fj/demo/test/JavaLinkedList.java +++ b/demo/src/main/java/fj/demo/test/JavaLinkedList.java @@ -1,6 +1,5 @@ package fj.demo.test; -import fj.F2; import static fj.test.Arbitrary.arbInteger; import static fj.test.Arbitrary.arbLinkedList; import static fj.test.CheckResult.summary; @@ -18,7 +17,7 @@ public final class JavaLinkedList { public static void main(final String[] args) { final Property p = property(arbLinkedList(arbInteger), arbLinkedList(arbInteger), (x, y) -> { - final LinkedList xy = new LinkedList(x); + final LinkedList xy = new LinkedList<>(x); xy.addAll(y); return prop(xy.size() == x.size() + y.size()); }); diff --git a/demo/src/main/java/fj/demo/test/ListFunctorLaws.java b/demo/src/main/java/fj/demo/test/ListFunctorLaws.java index b2c230a5..46f7bdb0 100644 --- a/demo/src/main/java/fj/demo/test/ListFunctorLaws.java +++ b/demo/src/main/java/fj/demo/test/ListFunctorLaws.java @@ -1,7 +1,5 @@ package fj.demo.test; -import fj.F; -import fj.F3; import fj.Function; import static fj.Function.compose; import fj.P2; @@ -17,8 +15,8 @@ import fj.function.Effect1; import fj.test.CheckResult; import static fj.test.CheckResult.summary; -import static fj.test.Coarbitrary.coarbInteger; -import static fj.test.Coarbitrary.coarbLong; +import static fj.test.Cogen.cogenInteger; +import static fj.test.Cogen.cogenLong; import fj.test.Property; import static fj.test.Property.prop; import static fj.test.Property.property; @@ -39,12 +37,12 @@ For any list, mapping the identity function (\x -> x) produces the same list. Note that to test this second law requires the generation of arbitrary functions. */ -@SuppressWarnings({"PackageVisibleField"}) +@SuppressWarnings("PackageVisibleField") @CheckParams(minSuccessful = 1000) public final class ListFunctorLaws { - final Property identity = property(arbList(arbString), x -> prop(listEqual(stringEqual).eq(x, x.map(Function.identity())))); + final Property identity = property(arbList(arbString), x -> prop(listEqual(stringEqual).eq(x, x.map(Function.identity())))); - final Property composition = property(arbF(coarbInteger, arbString), arbF(coarbLong, arbInteger), arbList(arbLong), (f, g, x) -> { + final Property composition = property(arbF(cogenInteger, arbString), arbF(cogenLong, arbInteger), arbList(arbLong), (f, g, x) -> { final List s1 = x.map(compose(f, g)); final List s2 = x.map(g).map(f); return prop(listEqual(stringEqual).eq(s1, s2)); @@ -54,11 +52,9 @@ public final class ListFunctorLaws { // composition: OK, passed 1000 tests. @SuppressWarnings("unchecked") public static void main(final String[] args) { - check(ListFunctorLaws.class).foreachDoEffect(new Effect1>() { - public void f(final P2 r) { - System.out.print(r._1() + ": "); - summary.println(r._2()); - } + check(ListFunctorLaws.class).foreachDoEffect(r -> { + System.out.print(r._1() + ": "); + summary.println(r._2()); }); } } diff --git a/demo/src/main/java/fj/demo/test/OptionMonadFunctorLaw.java b/demo/src/main/java/fj/demo/test/OptionMonadFunctorLaw.java index 675c8eff..5e5550a3 100644 --- a/demo/src/main/java/fj/demo/test/OptionMonadFunctorLaw.java +++ b/demo/src/main/java/fj/demo/test/OptionMonadFunctorLaw.java @@ -1,7 +1,5 @@ package fj.demo.test; -import fj.F; -import fj.F2; import static fj.Function.andThen; import fj.data.Option; import static fj.Equal.optionEqual; @@ -21,7 +19,7 @@ For any Option (o) and any function (f), then calling flatMap on o with a */ public final class OptionMonadFunctorLaw { public static void main(final String[] args) { - final Property unitMap = property(arbOption(arbInteger), Arbitrary.arbFInvariant(arbString), (o, f) -> prop(optionEqual(stringEqual).eq(o.bind(andThen(f, Option.some_())), o.map(f)))); + final Property unitMap = property(arbOption(arbInteger), Arbitrary.arbFInvariant(arbString), (o, f) -> prop(optionEqual(stringEqual).eq(o.bind(andThen(f, Option.some_())), o.map(f)))); summary.println(unitMap.minSuccessful(500)); // OK, passed 500 tests. } } diff --git a/demo/src/main/java/fj/demo/test/Reflect.java b/demo/src/main/java/fj/demo/test/Reflect.java index 8831314e..78084b47 100644 --- a/demo/src/main/java/fj/demo/test/Reflect.java +++ b/demo/src/main/java/fj/demo/test/Reflect.java @@ -2,8 +2,6 @@ import static fj.Bottom.error; -import fj.F; -import fj.F2; import fj.P2; import fj.data.List; import static fj.data.List.list; @@ -82,7 +80,7 @@ Property leave() { throw error("this should not be executed"); } - @SuppressWarnings({"UnusedDeclaration"}) + @SuppressWarnings("UnusedDeclaration") Property leave(final int i) { throw error("this should not be executed"); } @@ -121,11 +119,9 @@ public static void main(final String[] args) { } private static void printResults(final List> results) { - results.foreachDoEffect(new Effect1>() { - public void f(final P2 result) { - summary.print(result._2()); - out.println(" (" + result._1() + ')'); - } + results.foreachDoEffect(result -> { + summary.print(result._2()); + out.println(" (" + result._1() + ')'); }); out.println("--------------------------------------------------------------------------------"); } diff --git a/demo/src/main/java/fj/demo/test/StringBuilderReverse.java b/demo/src/main/java/fj/demo/test/StringBuilderReverse.java index 9338065b..78a4cc82 100644 --- a/demo/src/main/java/fj/demo/test/StringBuilderReverse.java +++ b/demo/src/main/java/fj/demo/test/StringBuilderReverse.java @@ -1,7 +1,5 @@ package fj.demo.test; -import fj.F; -import fj.F2; import static fj.data.List.list; import static fj.Equal.stringBuilderEqual; import static fj.test.Arbitrary.arbCharacter; @@ -36,10 +34,6 @@ public static void main(final String[] args) { //OK, passed 100 tests. //OK, passed 100 tests. //OK, passed 100 tests. - list(p1, p2, p3).foreachDoEffect(new Effect1() { - public void f(final Property p) { - summary.println(p.check()); - } - }); + list(p1, p2, p3).foreachDoEffect(p -> summary.println(p.check())); } } diff --git a/demo/src/main/java/fj/demo/test/Triangulation.java b/demo/src/main/java/fj/demo/test/Triangulation.java index 56d110f2..10de64ea 100644 --- a/demo/src/main/java/fj/demo/test/Triangulation.java +++ b/demo/src/main/java/fj/demo/test/Triangulation.java @@ -1,6 +1,5 @@ package fj.demo.test; -import fj.F; import static fj.test.Arbitrary.arbInteger; import static fj.test.CheckResult.summary; import fj.test.Property; 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/etc/CONTRIBUTORS b/etc/CONTRIBUTORS index 63be1827..06536883 100644 --- a/etc/CONTRIBUTORS +++ b/etc/CONTRIBUTORS @@ -214,3 +214,14 @@ jb@giraudeau.info Zheka Kozlov @orionll +@mrbackend + +Brian McKenna + +Clinton Selke + +Ryan Johnson + +Charles O'Farrell + +Fabian Kramer diff --git a/etc/release-notes/release-notes-4.6.adoc b/etc/release-notes/release-notes-4.6.adoc index d9bc2b3d..a33c1214 100644 --- a/etc/release-notes/release-notes-4.6.adoc +++ b/etc/release-notes/release-notes-4.6.adoc @@ -1,28 +1,74 @@ = Release 4.6 -Proposed release: Jun 2016 +Released: 3 July 2016 == Enhancements -* TODO. +* Added various list permutation generation for Gen (#235). +* Reduced memory for hard memos (#257). +* Added a priority queue based on finger trees (#261). +* Construct a database action from a Connection to a value (#267). +* Added methods and instances on Natural for Equal, Hash, Arbitrary, Shirnk, equals, hashCode and toString (#274). +* Use BigIntegers for Natural Gen and Shrink, instead of Long (#278). +* Various optics for Option (#244). +* First class catamorphism for Either and Option (#286). +* Added IO walkthrough (#254). +* Added numerous Semigroup methods, Monoid.dual, conversion from Ord to Semigroup and NonEmptyList folds (#251). +* Added ignored Optional optic (#250). +* Added IO methods: as, voided, when and unless (#245). +* Added monoid instances for io and unit; added semigroup instances for first, last and io (#246). == Fixes -* TODO. +* Fixed Gen.pick (#235). Affects Gen.variant, Coarbitrary and dependencies. +* Fixed Rand.reseed (#237). +* IOFunctions.bracket runs finaliser (#240). +* Fixed backwards List.traverseIO (#248). +* Run IOW map, bind and append lazily (#252, 253). +* Make State class stack safe (#259). +* Removed extra type parameters in P2.Optic (#273). +* Fixed BigInteger.shrink (#276, 277). == Internal -* TODO. +* Upgrade to Gradle 2.13 (#243). +* Document version deprecated for all deprecated methods (#264). +* Fixed various unchecked conversion warnings (#269). +* Added tests for DbState (#234). +* Added test for quickcheck Bool (#233). +* Various improvements (using final classes, method references, diamond operators, type inference, import lists, replace anonymous classes with lambdas etc. - #238). +* Use Either catamorphism internally (#285). +* Use uncurried foldLeft as default implementation (#283). +* Removed workarounds for fixed javac bugs (#282). +* Added IO test (#272). +* Exclude the demo subproject from Coveralls code coverage metrics (#268). +* Added tests for fj.Class (#262). +* Use method reference in IOWalkthrough (#255). +* Update retro lambda plugin version from 3.2.0 to 3.2.5 (#247). == Breaking Changes -* TODO. +* Removed deprecated Equal.shallowEquals0 (#239). +* Removed use of Arbitrary wrapper (#271). + +== Deprecations + +* Array.toJavaArray (#241). == Documentation -* TODO. +* Added IO walkthrough (as per enhancements, #254). == Contributors -* TODO. +* Mark Perry +* JB Giraudeau +* @mrbackend +* Brian McKenna +* Clinton Selke +* Gabor Liptak +* Ryan Johnson +* Charles O'Farrell +* Fabian Kramer + diff --git a/etc/release-notes/release-notes-4.7.adoc b/etc/release-notes/release-notes-4.7.adoc new file mode 100644 index 00000000..428a0726 --- /dev/null +++ b/etc/release-notes/release-notes-4.7.adoc @@ -0,0 +1,58 @@ + += Release 4.7 + +Released: 27 March 2017 + +== Enhancements + +* 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 + +* 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 + +* 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 + +* none. + +== Documentation + +* none. + +== Contributors + +* Jean-Baptiste Giradeau +* Mark Perry +* Gabor Liptak +* Shimi Bandiel +* Siro Mateos + 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 new file mode 100644 index 00000000..7e8aed61 --- /dev/null +++ b/etc/release-notes/release-notes-4.8.adoc @@ -0,0 +1,53 @@ + += Release 4.8 + +Released: 18 Aug 2018 + +== Enhancements + +- 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 + +- 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 + +- 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 + +None. + +== Documentation + +None. + +== Contributors + +* Jean Baptiste Giraudeau +* Ryan Johnson +* l1cache (cache@bk.ru) +* Gabor Liptak +* janbols +* Iaroslav Zeigerman +* Signey Quitorio + 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 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..86cfc69e --- /dev/null +++ b/etc/release-notes/release-notes-5.0.adoc @@ -0,0 +1,49 @@ + += Release 5.0 + +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 + 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 f62cb01f..f5699a5b 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -1,16 +1,16 @@ -Current Release Process -======================= +Release Process +=============== -Update build.gradle: -* set isSnapshot to false -* set useRetroLambda to true +Go through the issues and pull requests and set the Label and Milestone field. Add information to /etc/release-notes/release-notes-.adoc. Update gradle.properties: +* set isSnapshot to false * Set signingEnabled to true + 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,12 +18,13 @@ 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 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. @@ -33,9 +34,75 @@ 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. + +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: + +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" + +Create the file %UserProfile%\.gradle\gradle.properties, set the values below: + +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..e96743fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,12 @@ +signingEnabled = false + sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signingEnabled = false +#signing.keyId= +#signing.password= +#signing.secretKeyRingFile= + +org.gradle.parallel = true +org.gradle.caching = true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3c7abdf1..5c2d1cf0 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be138e88..b1159fc5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue Jun 10 12:02:45 EST 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/gradlew b/gradlew index 91a7e269..83f2acfd 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# 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='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +75,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# 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\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +105,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 @@ -110,10 +125,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +170,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..24467a14 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,14 +24,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="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +62,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 +75,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/java-core/build.gradle b/java-core/build.gradle index 88e9a25f..f6a6146a 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -3,14 +3,15 @@ archivesBaseName = "${project.projectName}-${project.name}" ext { signModule = true + uploadModule = true } dependencies { - compile project(":core") - testCompile dependencyJunit + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) +configureUpload(signingEnabled, signModule, uploadModule) -uploadArchives.enabled = true 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/java8/build.gradle b/java8/build.gradle deleted file mode 100644 index 88e9a25f..00000000 --- a/java8/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ - -archivesBaseName = "${project.projectName}-${project.name}" - -ext { - signModule = true -} - -dependencies { - compile project(":core") - testCompile dependencyJunit -} - -performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - -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 dbaebe4d..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,66 +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' } } } + } + } - } - } -} -void configureAllRetroLambda() { - configureRetroLambda(useRetroLambda, newJdkEnvVar, oldJdkEnvVar, retroLambdaTarget) -} + repositories { + maven { + url project.sonatypeUploadUrl + credentials { + username sonatypeUsername + password sonatypePassword + } + } + } -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 + 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 { @@ -98,6 +154,4 @@ ext { 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..7efbde2e 100644 --- a/performance/build.gradle +++ b/performance/build.gradle @@ -1,7 +1,6 @@ -configureAllRetroLambda() - dependencies { - compile project(":core") - testCompile dependencyJunit + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime } 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-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 4d1a04e9..396d19d4 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -4,27 +4,33 @@ archivesBaseName = "${project.projectName}-${project.name}" apply plugin: 'scala' ext { - scalaVersion = "2.11.7" - scalacheckScalaVersion = "2.11" - scalacheckVersion = "1.12.4" - signModule = true -} +// 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.14.0" +// scalacheckVersion = "1.15.2" -tasks.withType(ScalaCompile) { - scalaCompileOptions.useAnt = false + signModule = true } 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 dependencyJunit + testImplementation junitCompile + testRuntimeOnly junitRuntime } -sourceCompatibility = "1.7" +tasks.withType(ScalaCompile) { + scalaCompileOptions.additionalParameters = ["-feature", "-language:implicitConversions", "-language:postfixOps"] +} performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - - +configureUpload(signingEnabled, signModule, project.uploadModule) 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..aca5cbd1 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala @@ -0,0 +1,18 @@ +package fj.data + +import org.scalacheck.Arbitrary + +/** + * + */ +object ArbitraryIO { + + + private case class ArbIO[T](value: T) extends IO[T] { + override def run(): T = value + } + + implicit def arbitraryIO[T](implicit arbT: Arbitrary[T]): Arbitrary[IO[T]] = + Arbitrary(arbT.arbitrary.map(ArbIO(_))) + +} 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))) +} diff --git a/props-core-scalacheck/src/main/scala/fj/package.scala b/props-core-scalacheck/src/main/scala/fj/package.scala index 5a3b9bfc..78afe491 100644 --- a/props-core-scalacheck/src/main/scala/fj/package.scala +++ b/props-core-scalacheck/src/main/scala/fj/package.scala @@ -1,5 +1,5 @@ import fj.Equal -import fj.data.List +import fj.data.{Option, List} package object fj { implicit def Function1F[A, B](g: A => B): F[A, B] = new F[A, B] { def f(a: A) = g(a) @@ -16,9 +16,11 @@ package object fj { implicit def intEqual: Equal[Int] = Equal.equal({(i1:Int, i2:Int) => Boolean.box((i1 == i2))}) implicit def unitEqual: Equal[Unit] = Equal.anyEqual() - + implicit def listEqual[A](implicit aEq: Equal[A]): Equal[List[A]] = Equal.listEqual(aEq) - + + implicit def optionEqual[A](implicit aEq: Equal[A]): Equal[Option[A]] = Equal.optionEqual(aEq) + implicit def p2Equal[A, B](implicit aEq: Equal[A], bEq: Equal[B]): Equal[P2[A, B]] = Equal.p2Equal(aEq, bEq) } \ No newline at end of file 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..8c9fe547 --- /dev/null +++ b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala @@ -0,0 +1,212 @@ +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 fj.data.IO +import org.scalacheck.Arbitrary._ +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Properties} + +/** + * Scalacheck [[Properties]] parameterized 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)) + +} + +/** + * 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()) + +} + +/** + * 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 toBuilder(t: T): S = conversion(t) + + 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: 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 + }) + + 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 + }) + + property("sum(x)") = forAll((x: T, y: T) => + 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") { + + + 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(CheckIOSemigroup) + + 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(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..c5ec3977 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, @@ -17,28 +18,29 @@ 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) + // 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) } } } + } } 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)}) } 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 dd95dabf..c629a10f 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,110 @@ -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 scala.collection.JavaConverters._ + +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 + }) + } \ 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-scalacheck/src/test/scala/fj/data/CheckList.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckList.scala index 36bd99f0..d675db81 100644 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckList.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckList.scala @@ -187,7 +187,7 @@ object CheckList extends Properties("List") { join(a))) property("groupBy") = forAll((a: List[Int]) => { - val result = a.groupBy((x: Int) => (x % 2 == 0): lang.Boolean) + val result = a.groupBy((x: Int) => (x % 2 == 0): lang.Boolean, Ord.booleanOrd) result.get(true).forall((xs: List[Int]) => xs.forall((x: Int) => (x % 2 == 0): lang.Boolean): lang.Boolean) && result.get(false).forall((xs: List[Int]) => xs.forall((x: Int) => (x % 2 != 0): lang.Boolean): lang.Boolean) && a.map((x: Int) => (x % 2) == 0: lang.Boolean).nub().length() == result.size() diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala index 18d2cdf2..13059d3f 100644 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala @@ -1,9 +1,11 @@ package fj package data +import fj.data.optic.PrismLaws import org.scalacheck.Prop._ import ArbitraryOption.arbitraryOption import ArbitraryP.arbitraryP1 +import ArbitraryUnit._ import Equal.{optionEqual, stringEqual} import Unit.unit import Option.{none, some, join} @@ -84,4 +86,10 @@ object CheckOption extends Properties("Option") { a.isNone || optionEqual(stringEqual).eq( join(a), a.some)) + + property("Optic.pSome") = PrismLaws[Option[String], String](Option.Optic.pSome()) + + property("Optic.some") = PrismLaws[Option[String], String](Option.Optic.some()) + + property("Optic.none") = PrismLaws[Option[Unit], Unit](Option.Optic.none()) } diff --git a/props-core/build.gradle b/props-core/build.gradle index d697cff5..534a051b 100644 --- a/props-core/build.gradle +++ b/props-core/build.gradle @@ -1,9 +1,8 @@ archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() - dependencies { - compile project(":quickcheck") - testCompile dependencyJunit + api project(":quickcheck") + testImplementation junitCompile + testRuntimeOnly junitRuntime } diff --git a/props-core/src/test/java/fj/MemoisationTest.java b/props-core/src/test/java/fj/MemoisationTest.java index b3c2853b..605546ef 100644 --- a/props-core/src/test/java/fj/MemoisationTest.java +++ b/props-core/src/test/java/fj/MemoisationTest.java @@ -1,110 +1,88 @@ package fj; import fj.test.Property; +import fj.test.runner.PropertyTestRunner; import org.junit.Test; +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; import static org.junit.Assert.assertTrue; -/** - * Created by mperry on 14/07/2014. - */ +@RunWith(PropertyTestRunner.class) public class MemoisationTest { - @Test - public void test1() { - final Property p = property(arbInteger, a -> { - P1 t = P.p(a).memo(); - return prop(t._1() == t._1()); + public Property test1() { + return property(arbInteger, a -> { + P1 t = P.weakMemo(() -> a); + return prop(t._1().equals(t._1())).and(prop(t._1().equals(a))); }); - summary.println(p.check()); } - @Test - public void test2() { - final Property p = property(arbInteger, arbInteger, (a, b) -> { - P2 t = P.p(a, b).memo(); - return prop(t._1() == t._1() && t._2() == t._2()); + public Property test1_hardMemo() { + return property(arbString, a -> { + P1 t = P.hardMemo(() -> new String(a)); + return prop(t._1() == t._1()).and(prop(t._1().equals(a))); }); - summary.println(p.check()); - } - - static P2 pair = P.p(0, 0); - - static Integer count(int i) { - if (i == 1) { - pair = P.p(pair._1() + 1, pair._2()); - return pair._1(); - } else if (i == 2) { - pair = P.p(pair._1(), pair._2() + 1); - return pair._2(); - } else { - return -1; - } } @Test - public void testRecomputeP2() { - P2 t = P.lazy(u -> count(1), u -> count(2)).memo(); - System.out.println("tuple: " + t + " 1:" + t._1() + " 2: " + t._2()); - assertTrue(t._1() == t._1() && t._2() == t._2()); + public Property test2() { + 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) ); + }); } @Test - public void test3() { - final Property p = property(arbInteger, arbInteger, arbInteger, (a, b, c) -> { + public Property test3() { + return property(arbInteger, arbInteger, arbInteger, (a, b, c) -> { P3 t = P.p(a, b, c).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3()); }); - summary.println(p.check()); } @Test - public void test4() { - final Property p = property(arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d) -> { + public Property test4() { + return property(arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d) -> { P4 t = P.p(a, b, c, d).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3() && t._4() == t._4()); }); - summary.println(p.check()); } @Test - public void test5() { - final Property p = property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e) -> { + public Property test5() { + return property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e) -> { P5 t = P.p(a, b, c, d, e).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3() && t._4() == t._4() && t._5() == t._5()); }); - summary.println(p.check()); } @Test - public void test6() { - final Property p = property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f) -> { + public Property test6() { + return property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f) -> { P6 t = P.p(a, b, c, d, e, f).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3() && t._4() == t._4() && t._5() == t._5() && t._6() == t._6()); }); - summary.println(p.check()); } @Test - public void test7() { - final Property p = property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f, g) -> { + public Property test7() { + return property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f, g) -> { P7 t = P.p(a, b, c, d, e, f, g).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3() && t._4() == t._4() && t._5() == t._5() && t._6() == t._6() && t._7() == t._7()); }); - summary.println(p.check()); } @Test - public void test8() { - final Property p = property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f, g, h) -> { + public Property test8() { + return property(arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, arbInteger, (a, b, c, d, e, f, g, h) -> { P8 t = P.p(a, b, c, d, e, f, g, h).memo(); return prop(t._1() == t._1() && t._2() == t._2() && t._3() == t._3() && t._4() == t._4() && t._5() == t._5() && t._6() == t._6() && t._7() == t._7() && t._8() == t._8()); }); - summary.println(p.check()); } } 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..0c4f418f --- /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.function.Strings; +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, Strings.reverse().f(s3)); + return prop(Equal.streamEqual(stringEqual).eq(str.bind(f), pm.parFlatMap(str, f).claim())); + }); + } + + 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(Strings.reverse())), + pm.parFoldMap(str, Strings.reverse(), stringMonoid, chunk).claim() + )); + }); + } + + +} diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index 083716e9..48352620 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -5,17 +5,15 @@ 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.Coarbitrary.coarbInteger; +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.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 4/12/2014. - */ + public class ReaderTest { @Test @@ -24,7 +22,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 @@ -39,11 +36,11 @@ public void testFlatMap() { @Test public void testMapProp() { Property p = property( - arbF(coarbInteger, arbInteger), - arbF(coarbInteger, arbInteger), + arbF(cogenInteger, arbInteger), + 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)); }); @@ -52,13 +49,13 @@ public void testMapProp() { @Test public void testFlatMapProp() { - Arbitrary>> a = arbF(coarbInteger, arbReader()); + Gen>> a = arbF(cogenInteger, arbReader()); Property p = property( - arbF(coarbInteger, arbInteger), + arbF(cogenInteger, arbInteger), 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)); } @@ -72,7 +69,7 @@ public void testLeftIdentity() { Property p = Property.property( arbInteger, arbInteger, - arbF(coarbInteger, arbReader()), + arbF(cogenInteger, arbReader()), (i, j, f) -> { int a = Reader.constant(i).flatMap(f).f(j); int b = f.f(i).f(j); @@ -99,8 +96,8 @@ public void testAssociativity() { Property p = Property.property( arbInteger, arbReader(), - arbF(coarbInteger, arbReader()), - arbF(coarbInteger, arbReader()), + arbF(cogenInteger, arbReader()), + arbF(cogenInteger, arbReader()), (i, r, f, g) -> { boolean b2 = r.flatMap(f).flatMap(g).f(i) == r.flatMap(x -> f.f(x).flatMap(g)).f(i); return prop(b2); @@ -108,8 +105,21 @@ public void testAssociativity() { PropertyAssert.assertResult(p); } - public Arbitrary> arbReader() { - return Arbitrary.arbReader(coarbInteger, arbInteger); + @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/StateProperties.java b/props-core/src/test/java/fj/data/StateProperties.java new file mode 100644 index 00000000..c5ce2a2e --- /dev/null +++ b/props-core/src/test/java/fj/data/StateProperties.java @@ -0,0 +1,406 @@ +package fj.data; + +import fj.F; +import fj.P2; +import fj.Unit; +import fj.test.Cogen; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import static fj.P.p; +import static fj.data.List.range; +import static fj.test.Arbitrary.arbF; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbList; +import static fj.test.Arbitrary.arbP2; +import static fj.test.Cogen.cogenInteger; +import static fj.test.Cogen.cogenP2; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +public final class StateProperties { + + private static final Gen arbUnit = Gen.value(Unit.unit()); + private static final int HUGE_SIZE = 10000; + + private static boolean testUnit(F> runF, S initS) { + State instance = State.unit(runF); + P2 actual = instance.run(initS); + P2 expected = runF.f(initS); + return actual.equals(expected); + } + + private static boolean testInit(S initS) { + State instance = State.init(); + P2 actual = instance.run(initS); + P2 expected = p(initS, initS); + return actual.equals(expected); + } + + private static boolean testUnits(F f, S initS) { + State instance = State.units(f); + P2 actual = instance.run(initS); + S expectedS = f.f(initS); + P2 expected = p(expectedS, expectedS); + return actual.equals(expected); + } + + private static boolean testConstant(A a, S initS) { + State instance = State.constant(a); + P2 actual = instance.run(initS); + P2 expected = p(initS, a); + return actual.equals(expected); + } + + private static boolean testStaticGets(F f, S initS) { + State instance = State.gets(f); + P2 actual = instance.run(initS); + P2 expected = p(initS, f.f(initS)); + return actual.equals(expected); + } + + private static boolean testPut(S newS, S initS) { + State instance = State.put(newS); + P2 actual = instance.run(initS); + P2 expected = p(newS, Unit.unit()); + return actual.equals(expected); + } + + private static boolean testModify(F f, S initS) { + State instance = State.modify(f); + P2 actual = instance.run(initS); + P2 expected = p(f.f(initS), Unit.unit()); + return actual.equals(expected); + } + + private static boolean testStaticFlatMap(F> runF, F> f, S initS) { + State instance = State.flatMap(State.unit(runF), f); + P2 actual = instance.run(initS); + P2 intermediateExpected = runF.f(initS); + P2 expected = f.f(intermediateExpected._2()).run(intermediateExpected._1()); + return actual.equals(expected); + } + + private static boolean testSequence(List> states, S initS) { + State> instance = State.sequence(states); + P2> actual = instance.run(initS); + + S expectedFinalS = initS; + List expectedAs = List.nil(); + List> remainingStates = states; + while (remainingStates.isNotEmpty()) { + P2 nextResult = remainingStates.head().run(expectedFinalS); + expectedFinalS = nextResult._1(); + expectedAs = List.cons(nextResult._2(), expectedAs); + remainingStates = remainingStates.tail(); + } + expectedAs = expectedAs.reverse(); + + P2> expected = p(expectedFinalS, expectedAs); + return actual.equals(expected); + } + + private static boolean testTraverse(List as, F> f, S initS) { + State> instance = State.traverse(as, f); + P2> actual = instance.run(initS); + + S expectedFinalS = initS; + List expectedFinalBs = List.nil(); + List currAs = as; + while (currAs.isNotEmpty()) { + P2 nextStateAndB = f.f(currAs.head()).run(expectedFinalS); + expectedFinalS = nextStateAndB._1(); + expectedFinalBs = List.cons(nextStateAndB._2(), expectedFinalBs); + currAs = currAs.tail(); + } + expectedFinalBs = expectedFinalBs.reverse(); + P2> expected = p(expectedFinalS, expectedFinalBs); + + return actual.equals(expected); + } + + private static boolean testRun(F> runF, S initS) { + State instance = State.unit(runF); + P2 actual = instance.run(initS); + P2 expected = runF.f(initS); + return actual.equals(expected); + } + + private static boolean testEval(F> runF, S initS) { + State instance = State.unit(runF); + A actual = instance.eval(initS); + A expected = runF.f(initS)._2(); + return actual.equals(expected); + } + + private static boolean testExec(F> runF, S initS) { + State instance = State.unit(runF); + S actual = instance.exec(initS); + S expected = runF.f(initS)._1(); + return actual.equals(expected); + } + + private static boolean testGets(State state, S initS) { + State instance = state.gets(); + P2 actual = instance.run(initS); + P2 expected = p(state.run(initS)._1(), state.run(initS)._1()); + return actual.equals(expected); + } + + private static boolean testMap(State state, F f, S initS) { + State instance = state.map(f); + P2 actual = instance.run(initS); + P2 expected = state.run(initS).map2(f); + return actual.equals(expected); + } + + private static boolean testMapState(State state, F, P2> f, S initS) { + State instance = state.mapState(f); + P2 actual = instance.run(initS); + P2 expected = f.f(state.run(initS)); + return actual.equals(expected); + } + + private static boolean testWiths(State state, F f, S initS) { + State instance = state.withs(f); + P2 actual = instance.run(initS); + P2 expected = state.run(f.f(initS)); + return actual.equals(expected); + } + + private static boolean testFlatMap(State state, F> f, S initS) { + State instance = state.flatMap(f); + P2 actual = instance.run(initS); + P2 expected = f.f(state.run(initS)._2()).run(state.run(initS)._1()); + return actual.equals(expected); + } + + private static boolean testNoStackOverflow(State instance, S initS) { + instance.run(initS); + return true; + } + + private static Gen>> arbRunF( + Cogen cogenInitS, + Gen arbNextS, + Gen arbValue) { + + return arbF(cogenInitS, arbP2(arbNextS, arbValue)); + } + + private static Gen, P2>> arbMapStateF( + Cogen cogenInitS, + Cogen cogenInitValue, + Gen arbNextS, + Gen arbNextValue) { + + return arbF(cogenP2(cogenInitS, cogenInitValue), arbP2(arbNextS, arbNextValue)); + } + + private static Gen> arbState( + Cogen cogenInitS, + Gen arbNextS, + Gen arbValue) { + + Gen>> arbRunF = arbRunF(cogenInitS, arbNextS, arbValue); + return Gen.gen(s -> r -> State.unit(arbRunF.gen(s, r))); + } + + private static Gen> arbHugeState( + Gen> arbInitState, + F, Gen>> nextArbStateF) { + + return Gen.gen(s -> r -> range(0, HUGE_SIZE).foldLeft( + (acc, x) -> nextArbStateF.f(acc).gen(s, r), + arbInitState.gen(s, r))); + } + + public Property unit() { + return property( + arbRunF(cogenInteger, arbInteger, arbInteger), + arbInteger, + (runF, initS) -> prop(testUnit(runF, initS))); + } + + public Property init() { + return property( + arbInteger, + initS -> prop(testInit(initS))); + } + + public Property units() { + return property( + arbF(cogenInteger, arbInteger), + arbInteger, + (f, initS) -> prop(testUnits(f, initS))); + } + + public Property constant() { + return property( + arbInteger, + arbInteger, + (a, initS) -> prop(testConstant(a, initS))); + } + + public Property staticGets() { + return property( + arbF(cogenInteger, arbInteger), + arbInteger, + (f, initS) -> prop(testStaticGets(f, initS))); + } + + public Property put() { + return property( + arbInteger, + arbInteger, + (newS, initS) -> prop(testPut(newS, initS))); + } + + public Property modify() { + return property( + arbF(cogenInteger, arbInteger), + arbInteger, + (f, initS) -> prop(testModify(f, initS))); + } + + public Property staticFlatMap() { + return property( + arbRunF(cogenInteger, arbInteger, arbInteger), + arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)), + arbInteger, + (runF, f, initS) -> prop(testStaticFlatMap(runF, f, initS))); + } + + public Property sequence() { + return property( + arbList(arbState(cogenInteger, arbInteger, arbInteger)), + arbInteger, + (states, initS) -> prop(testSequence(states, initS))); + } + + public Property traverse() { + return property( + arbList(arbInteger), + arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)), + arbInteger, + (as, f, initS) -> prop(testTraverse(as, f, initS))); + + } + + public Property run() { + return property( + arbRunF(cogenInteger, arbInteger, arbInteger), + arbInteger, + (runF, initS) -> prop(testRun(runF, initS))); + } + + public Property eval() { + return property( + arbRunF(cogenInteger, arbInteger, arbInteger), + arbInteger, + (runF, initS) -> prop(testEval(runF, initS))); + } + + public Property exec() { + return property( + arbRunF(cogenInteger, arbInteger, arbUnit), + arbInteger, + (runF, initS) -> prop(testExec(runF, initS))); + } + + public Property getsProperty() { + return property( + arbState(cogenInteger, arbInteger, arbInteger), + arbInteger, + (state, initS) -> prop(testGets(state, initS))); + } + + public Property map() { + return property( + arbState(cogenInteger, arbInteger, arbInteger), + arbF(cogenInteger, arbInteger), + arbInteger, + (state, f, initS) -> prop(testMap(state, f, initS))); + } + + public Property mapState() { + return property( + arbState(cogenInteger, arbInteger, arbInteger), + arbMapStateF(cogenInteger, cogenInteger, arbInteger, arbInteger), + arbInteger, + (state, f, initS) -> prop(testMapState(state, f, initS))); + } + + public Property withs() { + return property( + arbState(cogenInteger, arbInteger, arbInteger), + arbF(cogenInteger, arbInteger), + arbInteger, + (state, f, initS) -> prop(testWiths(state, f, initS))); + } + + public Property flatMap() { + return property( + arbState(cogenInteger, arbInteger, arbInteger), + arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)), + arbInteger, + (state, f, initS) -> prop(testFlatMap(state, f, initS))); + } + + @CheckParams(minSuccessful = 1) + public Property getsStackSafety() { + return property( + arbHugeState( + arbState(cogenInteger, arbInteger, arbInteger), + currState -> Gen.value(currState.gets())), + arbInteger, + (instance, initS) -> prop(testNoStackOverflow(instance, initS))); + } + + @CheckParams(minSuccessful = 1) + public Property mapStackSafety() { + return property( + arbHugeState( + arbState(cogenInteger, arbInteger, arbInteger), + currState -> Gen.gen(s -> r -> currState.map(arbF(cogenInteger, arbInteger).gen(s, r)))), + arbInteger, + (instance, initS) -> prop(testNoStackOverflow(instance, initS))); + } + + @CheckParams(minSuccessful = 1) + public Property mapStateStackSafety() { + return property( + arbHugeState( + arbState(cogenInteger, arbInteger, arbInteger), + currState -> Gen.gen(s -> r -> + currState.mapState(arbMapStateF(cogenInteger, cogenInteger, arbInteger, arbInteger).gen(s, r)))), + arbInteger, + (instance, initS) -> prop(testNoStackOverflow(instance, initS))); + } + + @CheckParams(minSuccessful = 1) + public Property withsStackSafety() { + return property( + arbHugeState( + arbState(cogenInteger, arbInteger, arbInteger), + currState -> Gen.gen(s -> r -> currState.withs(arbF(cogenInteger, arbInteger).gen(s, r)))), + arbInteger, + (instance, initS) -> prop(testNoStackOverflow(instance, initS))); + } + + @CheckParams(minSuccessful = 1) + public Property flatMapStackSafety() { + return property( + arbHugeState( + arbState(cogenInteger, arbInteger, arbInteger), + currState -> Gen.gen(s -> r -> + currState.flatMap(arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)).gen(s, r)))), + arbInteger, + (instance, initS) -> prop(testNoStackOverflow(instance, initS))); + } + +} diff --git a/props-core/src/test/java/fj/data/TestRngState.java b/props-core/src/test/java/fj/data/TestRngState.java index b8a17494..41143c17 100644 --- a/props-core/src/test/java/fj/data/TestRngState.java +++ b/props-core/src/test/java/fj/data/TestRngState.java @@ -2,7 +2,7 @@ import fj.*; import fj.test.Arbitrary; -import fj.test.Coarbitrary; +import fj.test.Cogen; import fj.test.Gen; import fj.test.Property; import org.junit.Assert; @@ -12,14 +12,11 @@ import static fj.data.Stream.unfold; import static fj.data.test.PropertyAssert.assertResult; import static fj.test.Arbitrary.*; -import static fj.test.Coarbitrary.coarbInteger; +import static fj.test.Cogen.cogenInteger; import static fj.test.Property.prop; 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); @@ -79,20 +76,20 @@ public void testTraverse() { Assert.assertTrue(listIntEqual.eq(list, expected)); } - public static Arbitrary> arbState() { - return Arbitrary.arbState(Arbitrary.arbLcgRng(), Coarbitrary.coarbLcgRng(), arbInteger); + public static Gen> arbState() { + return Arbitrary.arbState(Arbitrary.arbLcgRng(), Cogen.cogenLcgRng(), arbInteger); } - public static Arbitrary>> arbStateF() { - return arbF(Coarbitrary.coarbLcgRng(), arbP2(arbLcgRng(), arbInteger)); + public static Gen>> arbStateF() { + return arbF(Cogen.cogenLcgRng(), arbP2(arbLcgRng(), arbInteger)); } - public static Coarbitrary> coarbState() { - return Coarbitrary.coarbState(Arbitrary.arbLcgRng(), (LcgRng s, Integer j) -> (long) (j >= 0 ? 2 * j : -2 * j + 1)); + public static Cogen> cogenState() { + return Cogen.cogenState(Arbitrary.arbLcgRng(), (LcgRng s, Integer j) -> (long) (j >= 0 ? 2 * j : -2 * j + 1)); } - public static Arbitrary>> arbBindable() { - return arbF(coarbInteger, arbState()); + public static Gen>> arbBindable() { + return arbF(cogenInteger, arbState()); } // Left identity: return i >>= f == f i diff --git a/props-core/src/test/java/fj/data/WriterTest.java b/props-core/src/test/java/fj/data/WriterTest.java index 158a2314..50134da3 100644 --- a/props-core/src/test/java/fj/data/WriterTest.java +++ b/props-core/src/test/java/fj/data/WriterTest.java @@ -2,22 +2,20 @@ 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; import org.junit.Test; import static fj.data.test.PropertyAssert.assertResult; import static fj.test.Arbitrary.*; -import static fj.test.Coarbitrary.coarbInteger; +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. - */ public class WriterTest { @Test @@ -45,7 +43,7 @@ public void testTellProp() { @Test public void testMap() { - Property p = property(arbInteger, arbF(coarbInteger, arbInteger), (i, f) -> { + Property p = property(arbInteger, arbF(cogenInteger, arbInteger), (i, f) -> { boolean b = eq.eq(defaultWriter.f(i).map(f), defaultWriter.f(f.f(i))); return prop(b); }); @@ -54,7 +52,7 @@ public void testMap() { @Test public void testFlatMap() { - Property p = property(arbInteger,arbF(coarbInteger, arbWriterStringInt()), (i, f) -> { + Property p = property(arbInteger,arbF(cogenInteger, arbWriterStringInt()), (i, f) -> { boolean b = eq.eq(Writer.stringLogger().f(i).flatMap(f), f.f(i)); return prop(b); }); @@ -62,12 +60,12 @@ public void testFlatMap() { } - public Arbitrary> arbWriterStringInt() { + public Gen> arbWriterStringInt() { return arbWriterString(arbInteger); } - public Arbitrary> arbWriterString(Arbitrary arb) { - return Arbitrary.arbitrary(arb.gen.map(a -> Writer.stringLogger().f(a))); + public Gen> arbWriterString(Gen arb) { + return arb.map(a -> Writer.stringLogger().f(a)); } // Left identity: return a >>= f == f a @@ -75,7 +73,7 @@ public Arbitrary> arbWriterString(Arbitrary arb) { public void testLeftIdentity() { Property p = Property.property( arbInteger, - arbF(coarbInteger, arbWriterStringInt()), + arbF(cogenInteger, arbWriterStringInt()), (i, f) -> { return prop(eq.eq(defaultWriter.f(i).flatMap(f), f.f(i))); }); @@ -97,8 +95,8 @@ public void testRightIdentity() { public void testAssociativity() { Property p = Property.property( arbWriterStringInt(), - arbF(coarbInteger, arbWriterStringInt()), - arbF(coarbInteger, arbWriterStringInt()), + arbF(cogenInteger, arbWriterStringInt()), + arbF(cogenInteger, arbWriterStringInt()), (w, f, g) -> { boolean t = eq.eq(w.flatMap(f).flatMap(g), w.flatMap(x -> f.f(x).flatMap(g))); return prop(t); @@ -106,7 +104,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("+")); + } } 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 a4265be6..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,5 +1,6 @@ package fj.data.fingertrees; +import fj.data.Stream; import fj.test.Property; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; @@ -10,17 +11,22 @@ 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 { - public Property size() { + Property size() { return property(arbList(arbInteger), list -> prop(list.foldLeft((acc, i) -> acc.snoc(i), FingerTree.emptyIntAddition()).length() == list.length()) ); } + Property stream() { + return property(arbList(arbInteger), list -> { + Stream s1 = list.foldLeft((acc, i) -> acc.snoc(i), FingerTree.emptyIntAddition()).toStream(); + Stream s2 = list.toStream(); + return prop(s1.equals(s2)); + }); + } + } 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 3d95d182..8cc94825 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -1,30 +1,99 @@ package fj.data.fingertrees; +import fj.Function; +import fj.P; +import fj.P2; import fj.data.List; +import fj.data.Option; import org.junit.Test; -import static fj.test.Property.prop; -import static fj.test.Property.property; +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; +import static org.hamcrest.MatcherAssert.assertThat; -/** - * Created by MarkPerry on 10/10/2015. - */ public class FingerTreeTest { + public static final int SIZE = 10; + @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); + } + + @Test + public void testSeqString() { + String actual = midSeq().toString(); + String expected = "Deep(9 -> One(1 -> 1), Deep(6 -> One(3 -> Node3(3 -> V3(2,3,4))), Empty(), One(3 -> Node3(3 -> V3(5,6,7)))), Two(2 -> V2(8,9)))"; + assertThat(actual, equalTo(expected)); + } + + public FingerTree> midPriorityQueue() { + FingerTree> ft = FingerTree.emptyIntMax(); + return List.range(1, SIZE).foldLeft(ft2 -> i -> { + int j = i % 2 == 0 ? 2 * i : i; + FingerTree> ft3 = ft2.snoc(P.p(j, j)); + return ft3; + }, ft); + } + + @Test + public void testQueueString() { + String actual = midPriorityQueue().toString(); + String expected = "Deep(16 -> One(1 -> (1,1)), Deep(12 -> One(8 -> Node3(8 -> V3((4,4),(3,3),(8,8)))), Empty(), One(12 -> Node3(12 -> V3((5,5),(12,12),(7,7))))), Two(16 -> V2((16,16),(9,9))))"; + assertThat(actual, equalTo(expected)); + } + + @Test + public void stream() { + FingerTree ft = midSeq(); + assertThat(ft.toStream().toList(), equalTo(List.range(1, SIZE))); + } + + @Test + public void split() { + int splitPoint = 3; + FingerTree ft = FingerTree.emptyIntAddition(); + FingerTree ft3 = List.range(1, SIZE).foldLeft(ft2 -> i -> ft2.snoc(i), ft); + P2, FingerTree> p = ft3.split(v -> v >= splitPoint); + assertThat(p._1().toStream().toList(), equalTo(List.range(1, splitPoint))); + assertThat(p._2().toStream().toList(), equalTo(List.range(splitPoint, SIZE))); + + } + } diff --git a/props-core/src/test/java/fj/data/hamt/BitSetProperties.java b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java new file mode 100644 index 00000000..fbda987b --- /dev/null +++ b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java @@ -0,0 +1,218 @@ +package fj.data.hamt; + +import fj.Equal; +import fj.Ord; +import fj.P; +import fj.P3; +import fj.data.List; +import fj.data.Seq; +import fj.data.test.PropertyAssert; +import fj.function.Booleans; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; + +import static fj.Equal.bitSetSequal; +import static fj.Equal.booleanEqual; +import static fj.Equal.listEqual; +import static fj.Equal.stringEqual; +import static fj.Function.identity; +import static fj.data.hamt.BitSet.MAX_BIT_SIZE; +import static fj.data.hamt.BitSet.listBitSet; +import static fj.data.hamt.BitSet.longBitSet; +import static fj.test.Arbitrary.arbBoolean; +import static fj.test.Arbitrary.arbLong; +import static fj.test.Property.impliesBoolean; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class BitSetProperties { + + Property andTest() { + return property(arbLong, arbLong, (a, b) -> prop(longBitSet(a).and(longBitSet(b)).longValue() == (a & b))); + } + + Property asStringTest() { + return property(arbLong, a -> prop(longBitSet(a).asString().equals(Long.toBinaryString(a)))); + } + + Property bitsToRightTest() { + return property(arbLong, arbBitSetSize, (a, i) -> + prop( + longBitSet(a).bitsToRight(i) == + longBitSet(a).toList().reverse().take(i).filter(identity()).length() + )); + } + + Property longRoundTripTest() { + return property(arbNaturalLong, l -> prop(longBitSet(l).longValue() == l)); + } + + Property empty() { + return prop(BitSet.empty().isEmpty() && BitSet.empty().longValue() == 0); + } + + Property generalEmptinessTest() { + return property(arbListBoolean, list -> + prop(list.dropWhile(Booleans.not).isEmpty() == listBitSet(list).isEmpty()) + ); + } + + Property foldLeftTest() { + return property(arbLong, l -> prop( + BitSet.longBitSet(l).toList().dropWhile(b -> !b).foldLeft( + (acc, b) -> acc + 1, 0 + ) == BitSet.longBitSet(l).bitsUsed() + )); + } + + Property fromListTest() { + return property(arbListBoolean, l -> prop(listBitSet(l).toList().equals(l.dropWhile(b -> !b)))); + } + + Property fromStreamTest() { + return property(arbListBoolean, l -> prop(listBitSet(l).toStream().toList().equals(l.dropWhile(b -> !b)))); + } + + Property fromLongTest() { + return property(arbLong, l -> prop(BitSet.longBitSet(l).longValue() == l)); + } + + Property fromStringTest() { + Gen g = arbListBoolean.map(l -> l.map(b -> Integer.toString(BitSet.toInt(b))).foldLeft((acc, s) -> acc + s, "")); + return property(g, (s) -> { + boolean zeroLength = s.isEmpty(); + return Property.implies(!zeroLength, () -> { + long x = new BigInteger(s, 2).longValue(); + long y = BitSet.stringBitSet(s).longValue(); + return prop(x == y); + }); + }); + } + + Gen> arbListBoolean = Gen.choose(0, MAX_BIT_SIZE).bind(i -> Gen.sequenceN(i, arbBoolean)); + + Property toListTest() { + return property(arbListBoolean, list -> { + List expected = list.dropWhile(Booleans.not); + List actual = listBitSet(list).toList(); + return prop(Equal.listEqual(Equal.booleanEqual).eq(expected, actual)); + }); + } + + Property clearTest() { + return property(arbLong, arbBitSetSize, (l, i) -> + prop(BitSet.longBitSet(l).clear(i).isSet(i) == false) + ); + } + + Property bitsUsedTest() { + return property(arbListBoolean, list -> prop( + list.dropWhile(b -> !b).length() == + listBitSet(list).bitsUsed() + )); + } + + Property isSetTest() { + return property(arbNaturalLong, Gen.choose(0, MAX_BIT_SIZE), + (Long l, Integer i) -> prop(longBitSet(l).isSet(i) == ((l & (1L << i)) != 0)) + ); + } + + Property stringBitSet() { + return property(arbNaturalLong, l -> + prop(BitSet.stringBitSet(BitSet.longBitSet(l).asString()).longValue() == l) + ); + } + + Property notTest() { + return property(arbLong, l -> prop(longBitSet(l).not().longValue() == ~l)); + } + + Property orTest() { + return property(arbLong, arbLong, (x, y) -> prop( + longBitSet(x).or(longBitSet(y)).longValue() == (x | y) + )); + } + + Gen> bitSetIndices(int n) { + return Gen.listOfSorted(Gen.choose(0, MAX_BIT_SIZE - 1), n, Ord.intOrd); + } + + Property rangeTest() { + return property(arbNaturalLong, bitSetIndices(4), (x, list) -> { + int l = list.index(0); + 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) && + bs2.isSet(vh - l) == false; + return prop(b); + }); + } + + + Property setTest() { + return property(arbNaturalLong, arbBitSetSize, (l, i) -> prop(longBitSet(l).set(i).isSet(i))); + } + + Property setBooleanTest() { + return property(arbNaturalLong, arbBitSetSize, arbBoolean, (l, i, b) -> prop(longBitSet(l).set(i, b).isSet(i) == b)); + } + + Property shiftLeftTest() { + return property(arbNaturalLong, arbBitSetSize, (l, i) -> { + BitSet bs = longBitSet(l); + boolean b = bs.shiftLeft(i).longValue() == (l << i); + return impliesBoolean(bs.bitsUsed() + i < MAX_BIT_SIZE, b); + }); + } + + Property shiftRightTest() { + return property(arbNaturalLong, arbBitSetSize, (l, i) -> { + return prop(longBitSet(l).shiftRight(i).longValue() == (l >> i)); + }); + } + + Property takeLowerTest() { + return property(arbNaturalLong, arbBitSetSize, (l, i) -> { + return prop(bitSetSequal.eq(longBitSet(l).takeLower(i), longBitSet(l).range(0, i))); + }); + } + + Property takeUpperTest() { + return property(arbNaturalLong, arbBitSetSize, (l, i) -> { + return prop(bitSetSequal.eq(longBitSet(l).takeUpper(i), longBitSet(l).range(MAX_BIT_SIZE, MAX_BIT_SIZE - i))); + }); + } + + Property toStreamTest() { + return property(arbNaturalLong, l -> { + return prop(listEqual(booleanEqual).eq(longBitSet(l).toList(), longBitSet(l).toStream().toList())); + }); + } + + Property xorTest() { + return property(arbNaturalLong, arbNaturalLong, (a, b) -> { + return prop(longBitSet(a).xor(longBitSet(b)).longValue() == (a ^ b)); + }); + } + static final Gen arbNaturalLong = Gen.choose(0, Long.MAX_VALUE); + + static final Gen arbBitSetSize = Gen.choose(0, MAX_BIT_SIZE - 1); + +} diff --git a/props-core/src/test/java/fj/data/hamt/HashArrayMappedTrieProperties.java b/props-core/src/test/java/fj/data/hamt/HashArrayMappedTrieProperties.java new file mode 100644 index 00000000..05001a1a --- /dev/null +++ b/props-core/src/test/java/fj/data/hamt/HashArrayMappedTrieProperties.java @@ -0,0 +1,94 @@ +package fj.data.hamt; + +import fj.Equal; +import fj.Hash; +import fj.Ord; +import fj.P2; +import fj.data.List; +import fj.data.Option; +import fj.data.Set; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import static fj.Equal.intEqual; +import static fj.Equal.optionEqual; +import static fj.Ord.intOrd; +import static fj.Ord.p2Ord; +import static fj.Ord.p2Ord2; +import static fj.data.Option.some; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbList; +import static fj.test.Arbitrary.arbListInteger; +import static fj.test.Arbitrary.arbP2; +import static fj.test.Arbitrary.arbSet; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +/** + * @author Mark Perry + */ +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 100) +public class HashArrayMappedTrieProperties { + + private static final HashArrayMappedTrie empty = HashArrayMappedTrie.emptyKeyInteger(); + private static final Gen>> arbListProducts = arbSet(intOrd, arbInteger).bind(s -> Gen.listOf(arbInteger, s.size()).map(list -> s.toList().zip(list))); + private static final Gen> arbHamt = arbListProducts.map(l -> empty.set(l)); + + Property empty() { + return prop(empty.isEmpty()); + } + + Property setFromList() { + return property(arbListProducts, list -> { + List> actual = empty.set(list).toList(intOrd); + List> expected = list.sort(p2Ord(intOrd, intOrd)); + boolean b = actual.equals(expected); + return prop(b); + }); + } + + Property overwriteKey() { + return property(arbHamt, arbInteger, arbInteger, arbInteger, (h, k, v1, v2) -> { + Option actual = h.set(k, v1).set(k, v2).find(k); + return prop(optionEqual(intEqual).eq(actual, some(v2))); + }); + } + + Property allIn() { + return property(arbListProducts, list -> { + HashArrayMappedTrie h = empty.set(list); + Boolean b = list.foldLeft((acc, p) -> h.find(p._1()).option(false, i -> true && acc), true); + return prop(b); + }); + } + + Property sampleInts() { + return property(arbListProducts, arbInteger, (ps, i) -> { + HashArrayMappedTrie h = empty.set(ps); + Option o1 = ps.find(p -> intEqual.eq(p._1(), i)).map(p -> p._2()); + Option o2 = h.find(i); + return prop(optionEqual(intEqual).eq(o1, o2)); + }); + } + + Property fold() { + return property(arbListProducts, list -> { + Integer actual = empty.set(list).foldLeft((acc, p) -> acc + p._2(), 0); + Integer expected = list.foldLeft((acc, p) -> acc + p._2(), 0); + return prop(intEqual.eq(actual, expected)); + }); + } + + Property length() { + return property(arbListProducts, list -> { + Integer actual = empty.set(list).length(); + Integer expected = list.length(); + return prop(intEqual.eq(actual, expected)); + }); + } + +} diff --git a/props-core/src/test/java/fj/data/properties/ArrayProperties.java b/props-core/src/test/java/fj/data/properties/ArrayProperties.java index c6aea391..feb5dcf4 100644 --- a/props-core/src/test/java/fj/data/properties/ArrayProperties.java +++ b/props-core/src/test/java/fj/data/properties/ArrayProperties.java @@ -3,7 +3,6 @@ import fj.*; import fj.data.Array; import fj.data.Either; -import fj.test.Arbitrary; import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; @@ -31,9 +30,9 @@ public class ArrayProperties { private static final Equal> eq = arrayEqual(intEqual); - private static final Arbitrary, Integer>> arbArrayWithIndex = arbitrary(arbArray(arbInteger).gen + private static final Gen, Integer>> arbArrayWithIndex = arbArray(arbInteger) .filter(Array::isNotEmpty) - .bind(array -> Gen.choose(0, array.length() - 1).map(i -> P.p(array, i)))); + .bind(array -> Gen.choose(0, array.length() - 1).map(i -> P.p(array, i))); public Property isEmpty() { return property(arbArray(arbInteger), array -> prop(array.isEmpty() != array.isNotEmpty())); diff --git a/props-core/src/test/java/fj/data/properties/ListProperties.java b/props-core/src/test/java/fj/data/properties/ListProperties.java index 825a5f17..fad5f66f 100644 --- a/props-core/src/test/java/fj/data/properties/ListProperties.java +++ b/props-core/src/test/java/fj/data/properties/ListProperties.java @@ -4,7 +4,6 @@ import fj.data.List; import fj.data.Stream; import fj.data.TreeMap; -import fj.test.Arbitrary; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; import fj.test.Gen; @@ -36,9 +35,9 @@ public class ListProperties { private static final Equal> eq = listEqual(intEqual); - private static final Arbitrary, Integer>> arbListWithIndex = arbitrary(arbList(arbInteger).gen + private static final Gen, Integer>> arbListWithIndex = arbList(arbInteger) .filter(List::isNotEmpty) - .bind(list -> Gen.choose(0, list.length() - 1).map(i -> p(list, i)))); + .bind(list -> Gen.choose(0, list.length() - 1).map(i -> p(list, i))); public Property isEmpty() { return property(arbList(arbInteger), list -> prop(list.isEmpty() != list.isNotEmpty())); @@ -172,11 +171,11 @@ public Property appendLength() { @CheckParams(minSize = 2, maxSize = 10000) public Property indexTail() { - final Gen, Integer>> gen = arbList(arbInteger).gen + final Gen, Integer>> gen = arbList(arbInteger) .filter(list -> list.length() > 1) .bind(list -> Gen.choose(1, list.length() - 1).map(i -> p(list, i))); - return property(Arbitrary.arbitrary(gen), pair -> { + return property(gen, pair -> { final List list = pair._1(); final int i = pair._2(); return prop(intEqual.eq(list.index(i), list.tail().index(i - 1))); @@ -256,7 +255,7 @@ public Property nub() { public Property groupBy() { return property(arbList(arbInteger), list -> { - final TreeMap> map = list.groupBy(i -> i % 2 == 0); + final TreeMap> map = list.groupBy(i -> i % 2 == 0, Ord.booleanOrd); final List list1 = map.get(true).orSome(nil()); final List list2 = map.get(false).orSome(nil()); return prop(list.length() == list1.length() + list2.length()) @@ -277,17 +276,17 @@ public Property groupByMonoid() { } public Property isPrefixOf() { - final Gen, Integer>> gen = arbList(arbInteger).gen.bind(list -> + final Gen, Integer>> gen = arbList(arbInteger).bind(list -> Gen.choose(0, list.length()).map(i -> p(list, i))); - return property(arbitrary(gen), pair -> prop(pair._1().take(pair._2()).isPrefixOf(intEqual, pair._1()))); + return property(gen, pair -> prop(pair._1().take(pair._2()).isPrefixOf(intEqual, pair._1()))); } public Property isSuffixOf() { - final Gen, Integer>> gen = arbList(arbInteger).gen.bind(list -> + final Gen, Integer>> gen = arbList(arbInteger).bind(list -> Gen.choose(0, list.length()).map(i -> p(list, i))); - return property(arbitrary(gen), pair -> prop(pair._1().drop(pair._2()).isSuffixOf(intEqual, pair._1()))); + return property(gen, pair -> prop(pair._1().drop(pair._2()).isSuffixOf(intEqual, pair._1()))); } public Property isPrefixOfShorter() { 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 new file mode 100644 index 00000000..5460bfdf --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java @@ -0,0 +1,115 @@ +package fj.data.properties; + +import fj.Ord; +import fj.P; +import fj.P2; +import fj.data.List; +import fj.data.Option; +import fj.data.PriorityQueue; +import fj.data.Set; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import static fj.data.Option.some; +import static fj.data.PriorityQueue.emptyInt; +import static fj.test.Arbitrary.arbAlphaNumString; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbList; +import static fj.test.Arbitrary.arbP2; +import static fj.test.Arbitrary.arbSet; +import static fj.test.Property.impliesBoolean; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 100) +public class PriorityQueueProperties { + + public static Gen> arbPriorityQueueIntegerString = arbQueue(arbAlphaNumString); + + /** + * Returns a queue with unique integer priorities. + */ + public static Gen> arbUniqueQueue(Gen aa) { + Gen> as = arbSet(Ord.intOrd, arbInteger); + Gen> ints = (as.map(si -> si.toList())); + Gen>> alp = ( + ints.bind(li -> aa.map(s -> li.map(i -> P.p(i, s)))) + ); + return (alp.map(l -> PriorityQueue.emptyInt().enqueue(l))); + } + + public static Gen> arbQueue(Gen aa) { + Gen>> g = arbList(arbP2(arbInteger, aa)); + return g.map(l -> PriorityQueue.emptyInt().enqueue(l)); + } + + Property empty() { + PriorityQueue pq = emptyInt(); + return prop(pq.isEmpty()); + } + + /** + * Adding a priority that is at the top and then removing it returns the original top. + */ + Property addRemove() { + return property(arbPriorityQueueIntegerString, arbInteger, arbAlphaNumString, (q, i, s) -> { + Option> t1 = q.top(); + Option> t2 = q.enqueue(i, s).dequeue().top(); + return prop(q.isLessThan(Ord.intOrd, i) ? + t1.equals(t2) : t2.map(p -> p._1() >= i).orSome(true) + ); + }); + } + + /** + * An empty queue has no top. + */ + Property emptyTop() { + return prop(emptyInt().top().isNone()); + } + + /** + * Adding a value with the highest priority makes it the top item. + */ + Property addTop() { + return property(arbPriorityQueueIntegerString, arbInteger, arbAlphaNumString, (q, i, s) -> { + Option> actual = q.enqueue(i, s).top(); + return prop(q.isLessThan(Ord.intOrd, i) ? actual.equals(some(P.p(i, s))) : actual.equals(q.top())); + }); + } + + /** + * Sorting a list returns the same as putting the list into a priority queue and getting the queue as a list. + */ + public Property sorted() { + return property(arbPriorityQueueIntegerString, pq -> { + List> expected = pq.toList().sort(Ord.p2Ord1(Ord.intOrd.reverse())); + List> actual = pq.toList(); + return prop(actual.equals(expected)); + }); + } + + /** + * Where the top n of the queue has just one element then: + * - Enqueueing and then topN of the queue should return a list of the top and the new item + * - Enqueuing and then dequeueing and then topping the queue should return the new item + */ + Property singleTopSame() { + return property(arbPriorityQueueIntegerString, arbAlphaNumString, (pq, s) -> { + Option o1 = pq.top().map(p -> p._1()); + return o1.map(j -> { + boolean b = pq.topN().length() == 1; + Property p1 = impliesBoolean(b, () -> pq.enqueue(j, s).dequeue().top().equals(some(P.p(j, s)))); + Property p2 = impliesBoolean(b, () -> pq.enqueue(j, s).topN().equals(List.list(pq.top().some(), P.p(j, s)))); + return p1.and(p2); + }).orSome(prop(true)); + }); + } + +} diff --git a/props-core/src/test/java/fj/data/properties/SeqProperties.java b/props-core/src/test/java/fj/data/properties/SeqProperties.java index 8e8076aa..eedbcc5c 100644 --- a/props-core/src/test/java/fj/data/properties/SeqProperties.java +++ b/props-core/src/test/java/fj/data/properties/SeqProperties.java @@ -6,7 +6,6 @@ import fj.data.Seq; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; -import fj.test.Arbitrary; import fj.test.Gen; import fj.test.Property; import org.junit.runner.RunWith; @@ -21,9 +20,9 @@ @CheckParams(maxSize = 10000) public class SeqProperties { - private static final Arbitrary, Integer>> arbSeqWithIndex = arbitrary(arbSeq(arbInteger).gen + private static final Gen, Integer>> arbSeqWithIndex = arbSeq(arbInteger) .filter(Seq::isNotEmpty) - .bind(seq -> Gen.choose(0, seq.length() - 1).map(i -> P.p(seq, i)))); + .bind(seq -> Gen.choose(0, seq.length() - 1).map(i -> P.p(seq, i))); public Property consHead() { return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.cons(n).head().equals(n))); @@ -107,12 +106,12 @@ public Property delete() { } public Property foldLeft() { - return property(arbSeq(arbitrary(Gen.value(1))), seq -> + return property(arbSeq(Gen.value(1)), seq -> prop(seq.foldLeft((acc, i) -> acc + i, 0) == seq.length())); } public Property foldRight() { - return property(arbSeq(arbitrary(Gen.value(1))), seq -> + return property(arbSeq(Gen.value(1)), seq -> prop(seq.foldRight((i, acc) -> acc + i, 0) == seq.length())); } 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 84c7e40c..2657b937 100644 --- a/props-core/src/test/java/fj/data/properties/SetProperties.java +++ b/props-core/src/test/java/fj/data/properties/SetProperties.java @@ -6,6 +6,7 @@ import fj.data.Set; import fj.data.Stream; import fj.test.Arbitrary; +import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; @@ -14,15 +15,12 @@ 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 { public final static int maxSize = 20; - public final static Arbitrary> as = Arbitrary.arbSet(Ord.intOrd, Arbitrary.arbInteger, maxSize); + public final static Gen> as = Arbitrary.arbSet(Ord.intOrd, Arbitrary.arbInteger, maxSize); public final static Equal> eq = Equal.listEqual(Equal.intEqual); Property setToListIsSorted() { diff --git a/props-core/src/test/java/fj/data/properties/StreamProperties.java b/props-core/src/test/java/fj/data/properties/StreamProperties.java index f68df0bc..07bc7897 100644 --- a/props-core/src/test/java/fj/data/properties/StreamProperties.java +++ b/props-core/src/test/java/fj/data/properties/StreamProperties.java @@ -3,7 +3,6 @@ import fj.*; import fj.data.Either; import fj.data.Stream; -import fj.test.Arbitrary; import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; @@ -170,11 +169,11 @@ public Property reverse() { @CheckParams(minSize = 2, maxSize = 10000) public Property indexTail() { - final Gen, Integer>> gen = arbStream(arbInteger).gen + final Gen, Integer>> gen = arbStream(arbInteger) .filter(stream -> stream.length() > 1) .bind(stream -> Gen.choose(1, stream.length() - 1).map(i -> P.p(stream, i))); - return property(Arbitrary.arbitrary(gen), pair -> { + return property(gen, pair -> { final Stream stream = pair._1(); final int i = pair._2(); return prop(intEqual.eq(stream.index(i), stream.tail()._1().index(i - 1))); 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 8dd718ee..50b323ff 100644 --- a/props-core/src/test/java/fj/data/properties/TreeMapProperties.java +++ b/props-core/src/test/java/fj/data/properties/TreeMapProperties.java @@ -6,7 +6,6 @@ import fj.data.List; import fj.data.Stream; import fj.data.TreeMap; -import fj.test.Arbitrary; import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; @@ -24,17 +23,14 @@ 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 { private static final int smallMax = 5; private static final int midMax = 20; - public static final Arbitrary> smallArbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, smallMax); - public static final Arbitrary> arbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, midMax); + public static final Gen> smallArbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, smallMax); + public static final Gen> arbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, midMax); public static final Ord> p2Ord = Ord.p2Ord1(Ord.intOrd); Equal>> listEq = Equal.listEqual(p2Equal(intEqual, stringEqual)); 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 d9580cc4..a47383ab 100644 --- a/props-core/src/test/java/fj/data/properties/ValidationProperties.java +++ b/props-core/src/test/java/fj/data/properties/ValidationProperties.java @@ -4,7 +4,7 @@ import fj.Semigroup; import fj.data.List; import fj.data.Validation; -import fj.test.Arbitrary; +import fj.test.Gen; import fj.test.Property; import fj.test.runner.PropertyTestRunner; import org.junit.runner.RunWith; @@ -13,14 +13,11 @@ 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 { public Property partition() { - Arbitrary>> al = arbList(arbValidation(arbUSASCIIString, arbInteger)); + Gen>> al = arbList(arbValidation(arbUSASCIIString, arbInteger)); return Property.property(al, list -> { P2, List> p = Validation.partition(list); boolean b1 = p._1().length() + p._2().length() == list.length(); @@ -31,7 +28,7 @@ public Property partition() { } public Property sequenceNonCumulative() { - Arbitrary>> al = arbList(arbValidation(arbUSASCIIString, arbInteger)); + Gen>> al = arbList(arbValidation(arbUSASCIIString, arbInteger)); return Property.property(al, list -> { Validation, List> v = Validation.sequenceNonCumulative(list); Property p1 = implies( diff --git a/quickcheck/build.gradle b/quickcheck/build.gradle index 4d3a25be..c9851596 100644 --- a/quickcheck/build.gradle +++ b/quickcheck/build.gradle @@ -1,18 +1,15 @@ ext { signModule = true + uploadModule = true } -configureAllRetroLambda() - archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile project(":core") - compile dependencyJunit + api project(":core") + api junitCompile } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - -uploadArchives.enabled = true +configureUpload(signingEnabled, signModule, uploadModule) diff --git a/quickcheck/src/main/java/fj/data/test/PropertyAssert.java b/quickcheck/src/main/java/fj/data/test/PropertyAssert.java index bec07f7f..d37c69a3 100644 --- a/quickcheck/src/main/java/fj/data/test/PropertyAssert.java +++ b/quickcheck/src/main/java/fj/data/test/PropertyAssert.java @@ -5,10 +5,9 @@ import fj.test.Property; import org.junit.Assert; -/** - * Created by MarkPerry on 18/12/2014. - */ -public class PropertyAssert { +public final class PropertyAssert { + + private PropertyAssert(){} public static Unit assertResult(Property p) { CheckResult cr = p.check(); diff --git a/quickcheck/src/main/java/fj/test/Arbitrary.java b/quickcheck/src/main/java/fj/test/Arbitrary.java index d46f4d4e..04f1e20b 100644 --- a/quickcheck/src/main/java/fj/test/Arbitrary.java +++ b/quickcheck/src/main/java/fj/test/Arbitrary.java @@ -13,6 +13,8 @@ import static fj.Function.compose; import static fj.P.p; + +import fj.P; import fj.P1; import fj.P2; import fj.P3; @@ -21,18 +23,19 @@ 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; -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.function.Effect1; +import fj.data.List; +import fj.data.Set; import static fj.data.Stream.range; import static fj.test.Gen.choose; @@ -50,30 +53,14 @@ import java.math.BigInteger; import java.sql.Time; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.GregorianCalendar; +import java.util.*; + +import static java.util.Locale.getAvailableLocales; +import static java.util.EnumSet.copyOf; + import java.util.HashMap; import java.util.HashSet; -import java.util.Hashtable; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Locale; -import static java.util.Locale.getAvailableLocales; import java.util.PriorityQueue; -import java.util.Properties; -import java.util.Stack; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.Vector; -import java.util.WeakHashMap; -import static java.util.EnumSet.copyOf; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -81,68 +68,65 @@ 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; /** - * The type used to generate arbitrary values of the given type parameter (A). Common - * arbitrary implementations are provided. + * Common Gen helper functions. * * @version %build.number% */ -public final class Arbitrary { - /** - * The generator associated with this arbitrary. - */ - @SuppressWarnings({"PublicField"}) - public final Gen gen; - - private Arbitrary(final Gen gen) { - this.gen = gen; - } - - /** - * Constructs and arbitrary with the given generator. - * - * @param g The generator to construct an arbitrary with. - * @return A new arbitrary that uses the given generator. - */ - public static Arbitrary arbitrary(final Gen g) { - return new Arbitrary(g); - } +public final class Arbitrary { /** * An arbitrary for functions. * - * @param c The coarbitrary for the function domain. + * @param c The cogen for the function domain. * @param a The arbitrary for the function codomain. * @return An arbitrary for functions. */ - public static Arbitrary> arbF(final Coarbitrary c, final Arbitrary a) { - return arbitrary(promote(new F>() { - public Gen f(final A x) { - return c.coarbitrary(x, a.gen); - } - })); + public static Gen> arbF(final Cogen c, final Gen a) { + return promote(x -> c.cogen(x, a)); } - public static Arbitrary> arbReader(Coarbitrary aa, Arbitrary ab) { - return arbitrary(Arbitrary.arbF(aa, ab).gen.map(f -> Reader.unit(f))); + public static Gen> arbReader(Cogen aa, Gen ab) { + return arbF(aa, ab).map(Reader::unit); } /** * An arbitrary for state. */ - public static Arbitrary> arbState(Arbitrary as, Coarbitrary cs, Arbitrary aa) { - return arbitrary(arbF(cs, arbP2(as, aa)).gen.map(f -> State.unit(f))); + 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. */ - public static Arbitrary arbLcgRng() { - return arbitrary(Arbitrary.arbLong.gen.map(l -> new LcgRng(l))); + public static Gen arbLcgRng() { + return Arbitrary.arbLong.map(LcgRng::new); } /** @@ -151,21 +135,21 @@ public static Arbitrary arbLcgRng() { * @param a The arbitrary for the function codomain. * @return An arbitrary for functions. */ - public static Arbitrary> arbFInvariant(final Arbitrary a) { - return arbitrary(a.gen.map(Function.constant())); + public static Gen> arbFInvariant(final Gen a) { + return a.map(Function.constant()); } /** * An arbitrary for function-2. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-2. */ - public static Arbitrary> arbF2(final Coarbitrary ca, final Coarbitrary cb, - final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, a)).gen.map(Function.uncurryF2())); + public static Gen> arbF2(final Cogen ca, final Cogen cb, + final Gen a) { + return arbF(ca, arbF(cb, a)).map(Function.uncurryF2()); } /** @@ -174,23 +158,22 @@ public static Arbitrary> arbF2(final Coarbitrary ca, fi * @param a The arbitrary for the function codomain. * @return An arbitrary for function-2. */ - public static Arbitrary> arbF2Invariant(final Arbitrary a) { - return arbitrary(a.gen.map( - compose(Function.uncurryF2(), compose(Function.>constant(), Function.constant())))); + public static Gen> arbF2Invariant(final Gen a) { + return a.map(compose(Function.uncurryF2(), compose(Function.constant(), Function.constant()))); } /** * An arbitrary for function-3. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-3. */ - public static Arbitrary> arbF3(final Coarbitrary ca, final Coarbitrary cb, - final Coarbitrary cc, final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, arbF(cc, a))).gen.map(Function.uncurryF3())); + public static Gen> arbF3(final Cogen ca, final Cogen cb, + final Cogen cc, final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, a))).map(Function.uncurryF3()); } /** @@ -199,27 +182,27 @@ public static Arbitrary> arbF3(final Coarbitrary * @param a The arbitrary for the function codomain. * @return An arbitrary for function-3. */ - public static Arbitrary> arbF3Invariant(final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF3(), compose(Function.>>constant(), + public static Gen> arbF3Invariant(final Gen a) { + return a.map(compose(Function.uncurryF3(), compose(Function.constant(), compose( - Function.>constant(), - Function.constant()))))); + Function.constant(), + Function.constant())))); } /** * An arbitrary for function-4. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. - * @param cd A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. + * @param cd A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-4. */ - public static Arbitrary> arbF4(final Coarbitrary ca, final Coarbitrary cb, - final Coarbitrary cc, final Coarbitrary cd, - final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, arbF(cc, arbF(cd, a)))).gen.map(Function.uncurryF4())); + public static Gen> arbF4(final Cogen ca, final Cogen cb, + final Cogen cc, final Cogen cd, + final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, arbF(cd, a)))).map(Function.uncurryF4()); } /** @@ -228,33 +211,32 @@ public static Arbitrary> arbF4(final Coarbitra * @param a The arbitrary for the function codomain. * @return An arbitrary for function-4. */ - public static Arbitrary> arbF4Invariant(final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF4(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant())))))); + public static Gen> arbF4Invariant(final Gen a) { + return a.map(compose(Function.uncurryF4(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + Function.constant()))))); } /** * An arbitrary for function-5. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. - * @param cd A coarbitrary for the part of the domain of the function. - * @param ce A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. + * @param cd A cogen for the part of the domain of the function. + * @param ce A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-5. */ - public static Arbitrary> arbF5(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Arbitrary a) { - return arbitrary( - arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, a))))).gen.map(Function.uncurryF5())); + public static Gen> arbF5(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, a))))).map(Function.uncurryF5()); } /** @@ -263,36 +245,36 @@ public static Arbitrary> arbF4Invariant(final * @param a The arbitrary for the function codomain. * @return An arbitrary for function-5. */ - public static Arbitrary> arbF5Invariant(final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF5(), - compose(Function.>>>>constant(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant()))))))); + public static Gen> arbF5Invariant(final Gen a) { + return a.map(compose(Function.uncurryF5(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + Function.constant())))))); } /** * An arbitrary for function-6. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. - * @param cd A coarbitrary for the part of the domain of the function. - * @param ce A coarbitrary for the part of the domain of the function. - * @param cf A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. + * @param cd A cogen for the part of the domain of the function. + * @param ce A cogen for the part of the domain of the function. + * @param cf A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-6. */ - public static Arbitrary> arbF6(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf, - final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, a)))))).gen.map( - Function.uncurryF6())); + public static Gen> arbF6(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf, + final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, a)))))).map( + Function.uncurryF6()); } /** @@ -301,39 +283,39 @@ public static Arbitrary> arbF4Invariant(final * @param a The arbitrary for the function codomain. * @return An arbitrary for function-6. */ - public static Arbitrary> arbF6Invariant(final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF6(), - compose(Function.>>>>>constant(), - compose(Function.>>>>constant(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant())))))))); + 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()))))))); } /** * An arbitrary for function-7. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. - * @param cd A coarbitrary for the part of the domain of the function. - * @param ce A coarbitrary for the part of the domain of the function. - * @param cf A coarbitrary for the part of the domain of the function. - * @param cg A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. + * @param cd A cogen for the part of the domain of the function. + * @param ce A cogen for the part of the domain of the function. + * @param cf A cogen for the part of the domain of the function. + * @param cg A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-7. */ - public static Arbitrary> arbF7(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf, - final Coarbitrary cg, - final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, arbF(cg, a))))))).gen.map( - Function.uncurryF7())); + public static Gen> arbF7(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf, + final Cogen cg, + final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, arbF(cg, a))))))).map( + Function.uncurryF7()); } /** @@ -342,42 +324,42 @@ public static Arbitrary> arbF4Invariant(final * @param a The arbitrary for the function codomain. * @return An arbitrary for function-7. */ - public static Arbitrary> arbF7Invariant(final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF7(), - compose(Function.>>>>>>constant(), - compose(Function.>>>>>constant(), - compose(Function.>>>>constant(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant()))))))))); + 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())))))))); } /** * An arbitrary for function-8. * - * @param ca A coarbitrary for the part of the domain of the function. - * @param cb A coarbitrary for the part of the domain of the function. - * @param cc A coarbitrary for the part of the domain of the function. - * @param cd A coarbitrary for the part of the domain of the function. - * @param ce A coarbitrary for the part of the domain of the function. - * @param cf A coarbitrary for the part of the domain of the function. - * @param cg A coarbitrary for the part of the domain of the function. - * @param ch A coarbitrary for the part of the domain of the function. + * @param ca A cogen for the part of the domain of the function. + * @param cb A cogen for the part of the domain of the function. + * @param cc A cogen for the part of the domain of the function. + * @param cd A cogen for the part of the domain of the function. + * @param ce A cogen for the part of the domain of the function. + * @param cf A cogen for the part of the domain of the function. + * @param cg A cogen for the part of the domain of the function. + * @param ch A cogen for the part of the domain of the function. * @param a An arbitrary for the codomain of the function. * @return An arbitrary for function-8. */ - public static Arbitrary> arbF8(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf, - final Coarbitrary cg, - final Coarbitrary ch, - final Arbitrary a) { - return arbitrary(arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, arbF(cg, arbF(ch, a)))))))).gen.map( - Function.uncurryF8())); + public static Gen> arbF8(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf, + final Cogen cg, + final Cogen ch, + final Gen a) { + return arbF(ca, arbF(cb, arbF(cc, arbF(cd, arbF(ce, arbF(cf, arbF(cg, arbF(ch, a)))))))).map( + Function.uncurryF8()); } /** @@ -386,279 +368,192 @@ public static Arbitrary> arbF4Invariant(final * @param a The arbitrary for the function codomain. * @return An arbitrary for function-8. */ - public static Arbitrary> arbF8Invariant( - final Arbitrary a) { - return arbitrary(a.gen.map(compose(Function.uncurryF8(), - compose(Function.>>>>>>>constant(), - compose(Function.>>>>>>constant(), - compose(Function.>>>>>constant(), - compose( - Function.>>>>constant(), - compose(Function.>>>constant(), - compose( - Function.>>constant(), - compose(Function.>constant(), - Function.constant())))))))))); + public static Gen> arbF8Invariant( + final Gen a) { + return a.map(compose(Function.uncurryF8(), + compose(Function.>>>>>>>constant(), + compose(Function.>>>>>>constant(), + compose(Function.>>>>>constant(), + compose( + Function.>>>>constant(), + compose(Function.>>>constant(), + compose( + Function.>>constant(), + compose(Function.>constant(), + Function.constant()))))))))); } /** * An arbitrary implementation for boolean values. */ - public static final Arbitrary arbBoolean = arbitrary(elements(true, false)); + public static final Gen arbBoolean = elements(true, false); /** * An arbitrary implementation for integer values. */ - public static final Arbitrary arbInteger = arbitrary(sized(new F>() { - public Gen f(final Integer i) { - return choose(-i, i); - } - })); + public static final Gen arbInteger = sized(i -> choose(-i, i)); /** * An arbitrary implementation for integer values that checks boundary values (0, 1, -1, * 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 Arbitrary arbIntegerBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbLong = - arbitrary(arbInteger.gen.bind(arbInteger.gen, new F>() { - public F f(final Integer i1) { - return new F() { - public Long f(final Integer i2) { - return (long) i1 << 32L & i2; - } - }; - } - })); + public static final Gen arbLong = + arbInteger.bind(arbInteger, i1 -> i2 -> (long) i1 << 32L & i2); /** * An arbitrary implementation for long values that checks boundary values (0, 1, -1, max, * 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 Arbitrary arbLongBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbByte = arbitrary(arbInteger.gen.map(new F() { - public Byte f(final Integer i) { - return (byte) i.intValue(); - } - })); + public static final Gen arbByte = arbInteger.map(i -> (byte) i.intValue()); /** * An arbitrary implementation for byte values that checks boundary values (0, 1, -1, max, * 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 Arbitrary arbByteBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbShort = arbitrary(arbInteger.gen.map(new F() { - public Short f(final Integer i) { - return (short) i.intValue(); - } - })); + public static final Gen arbShort = arbInteger.map(i -> (short) i.intValue()); /** * An arbitrary implementation for short values that checks boundary values (0, 1, -1, max, * 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 Arbitrary arbShortBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbCharacter = arbitrary(choose(0, 65536).map(new F() { - public Character f(final Integer i) { - return (char) i.intValue(); - } - })); + public static final Gen arbCharacter = choose(0, 65536).map(i -> (char) i.intValue()); /** * An arbitrary implementation for character values that checks boundary values (max, min, * max - 1, min + 1) with a frequency of 1% each then generates from {@link #arbCharacter} * the remainder of the time (96%). */ - public static final Arbitrary arbCharacterBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbDouble = arbitrary(sized(new F>() { - public Gen f(final Integer i) { - return choose((double) -i, i); - } - })); + public static final Gen arbDouble = sized(i -> choose((double) -i, i)); /** * An arbitrary implementation for double values that checks boundary values (0, 1, -1, max, * 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 Arbitrary arbDoubleBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbFloat = arbitrary(arbDouble.gen.map(new F() { - public Float f(final Double d) { - return (float) d.doubleValue(); - } - })); + public static final Gen arbFloat = arbDouble.map(d -> (float) d.doubleValue()); /** * An arbitrary implementation for float values that checks boundary values (0, 1, -1, max, * 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 Arbitrary arbFloatBoundaries = arbitrary(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.gen))); - } - })); + 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. */ - public static final Arbitrary arbString = - arbitrary(arbList(arbCharacter).gen.map(new F, String>() { - public String f(final List cs) { - return asString(cs); - } - })); + public static final Gen arbString = + arbList(arbCharacter).map(List::asString); /** * An arbitrary implementation for string values with characters in the US-ASCII range. */ - public static final Arbitrary arbUSASCIIString = - arbitrary(arbList(arbCharacter).gen.map(new F, String>() { - public String f(final List cs) { - return asString(cs.map(new F() { - public Character f(final Character c) { - return (char) (c % 128); - } - })); - } - })); + public static final Gen arbUSASCIIString = + arbList(arbCharacter).map(cs -> asString(cs.map(c -> (char) (c % 128)))); /** * An arbitrary implementation for string values with alpha-numeric characters. */ - public static final Arbitrary arbAlphaNumString = - arbitrary(arbList(arbitrary(elements(range(charEnumerator, 'a', 'z').append( + public static final Gen arbAlphaNumString = + arbList(elements(range(charEnumerator, 'a', 'z').append( range(charEnumerator, 'A', 'Z')).append( - range(charEnumerator, '0', '9')).toArray().array(Character[].class)))).gen.map(asString())); + range(charEnumerator, '0', '9')).toArray().array(Character[].class))).map(asString()); /** * An arbitrary implementation for string buffer values. */ - public static final Arbitrary arbStringBuffer = - arbitrary(arbString.gen.map(new F() { - public StringBuffer f(final String s) { - return new StringBuffer(s); - } - })); + public static final Gen arbStringBuffer = + arbString.map(StringBuffer::new); /** * An arbitrary implementation for string builder values. */ - public static final Arbitrary arbStringBuilder = - arbitrary(arbString.gen.map(new F() { - public StringBuilder f(final String s) { - return new StringBuilder(s); - } - })); + public static final Gen arbStringBuilder = + arbString.map(StringBuilder::new); /** * Returns an arbitrary implementation for generators. @@ -666,20 +561,13 @@ public StringBuilder f(final String s) { * @param aa an arbitrary implementation for the type over which the generator is defined. * @return An arbitrary implementation for generators. */ - public static Arbitrary> arbGen(final Arbitrary aa) { - return arbitrary(sized(new F>>() { - @SuppressWarnings({"IfMayBeConditional"}) - public Gen> f(final Integer i) { - if (i == 0) - return fail(); - else - return aa.gen.map(new F>() { - public Gen f(final A a) { - return value(a); - } - }).resize(i - 1); - } - })); + public static Gen> arbGen(final Gen aa) { + return sized(i -> { + if (i == 0) + return fail(); + else + return aa.map(Gen::value).resize(i - 1); + }); } /** @@ -688,18 +576,10 @@ public Gen f(final A a) { * @param aa an arbitrary implementation for the type over which the optional value is defined. * @return An arbitrary implementation for optional values. */ - public static Arbitrary> arbOption(final Arbitrary aa) { - return arbitrary(sized(new F>>() { - public Gen> f(final Integer i) { - return i == 0 ? - value(Option.none()) : - aa.gen.map(new F>() { - public Option f(final A a) { - return some(a); - } - }).resize(i - 1); - } - })); + public static Gen> arbOption(final Gen aa) { + return sized(i -> i == 0 ? + value(Option.none()) : + aa.map(Option::some).resize(i - 1)); } /** @@ -711,19 +591,11 @@ public Option f(final A a) { * defined. * @return An arbitrary implementation for the disjoint union. */ - @SuppressWarnings({"unchecked"}) - public static Arbitrary> arbEither(final Arbitrary aa, final Arbitrary ab) { - final Gen> left = aa.gen.map(new F>() { - public Either f(final A a) { - return left(a); - } - }); - final Gen> right = ab.gen.map(new F>() { - public Either f(final B b) { - return right(b); - } - }); - return arbitrary(oneOf(list(left, right))); + @SuppressWarnings("unchecked") + public static Gen> arbEither(final Gen aa, final Gen ab) { + final Gen> left = aa.map(Either::left); + final Gen> right = ab.map(Either::right); + return oneOf(list(left, right)); } /** @@ -732,47 +604,47 @@ public Either f(final B b) { * @param aa An arbitrary implementation for the type over which the list is defined. * @return An arbitrary implementation for lists. */ - public static Arbitrary> arbList(final Arbitrary aa) { - return arbitrary(listOf(aa.gen)); + public static Gen> arbList(final Gen aa) { + return listOf(aa); } /** * Returns an arbitrary list of integers. */ - public static Arbitrary> arbListInteger() { - return arbitrary(listOf(arbInteger.gen)); + public static Gen> arbListInteger() { + return listOf(arbInteger); } /** * Returns an arbitrary list of strings. */ - public static Arbitrary> arbListString() { - return arbitrary(listOf(arbString.gen)); + public static Gen> arbListString() { + return listOf(arbString); } /** * Returns an arbitrary list of booleans. */ - public static Arbitrary> arbListBoolean() { - return arbitrary(listOf(arbBoolean.gen)); + public static Gen> arbListBoolean() { + return listOf(arbBoolean); } /** * Returns an arbitrary list of doubles. */ - public static Arbitrary> arbListDouble() { - return arbitrary(listOf(arbDouble.gen)); + public static Gen> arbListDouble() { + return listOf(arbDouble); } - public static Arbitrary> arbNonEmptyList(final Arbitrary aa) { - return arbitrary(Gen.listOf1(aa.gen).map(list -> NonEmptyList.fromList(list).some())); + public static Gen> arbNonEmptyList(final Gen aa) { + return Gen.listOf1(aa).map(list -> NonEmptyList.fromList(list).some()); } /** * Returns an arbitrary Validation for the given arbitrary parameters. */ - public static Arbitrary> arbValidation(final Arbitrary aa, final Arbitrary ab) { - return arbitrary(arbBoolean.gen.bind(bool -> bool ? ab.gen.map(b -> Validation.success(b)) : aa.gen.map(a -> Validation.fail(a)))); + public static Gen> arbValidation(final Gen aa, final Gen ab) { + return arbBoolean.bind(bool -> bool ? ab.map(Validation::success) : aa.map(Validation::fail)); } /** @@ -781,12 +653,8 @@ public static Arbitrary> arbValidation(final Arbitrary Arbitrary> arbStream(final Arbitrary aa) { - return arbitrary(arbList(aa).gen.map(new F, Stream>() { - public Stream f(final List as) { - return as.toStream(); - } - })); + public static Gen> arbStream(final Gen aa) { + return arbList(aa).map(List::toStream); } /** @@ -795,12 +663,8 @@ public Stream f(final List as) { * @param aa An arbitrary implementation for the type over which the array is defined. * @return An arbitrary implementation for arrays. */ - public static Arbitrary> arbArray(final Arbitrary aa) { - return arbitrary(arbList(aa).gen.map(new F, Array>() { - public Array f(final List as) { - return as.toArray(); - } - })); + public static Gen> arbArray(final Gen aa) { + return arbList(aa).map(List::toArray); } /** @@ -810,17 +674,16 @@ public Array f(final List as) { * @return An arbitrary implementation for sequences. */ @SuppressWarnings("unchecked") - public static Arbitrary> arbSeq(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(array -> Seq.seq((A[]) array.array()))); + public static Gen> arbSeq(final Gen aa) { + return arbArray(aa).map(Seq::iterableSeq); } - public static Arbitrary> arbSet(Ord ord, final Arbitrary aa) { - return arbitrary(arbList(aa).gen.map(list -> Set.iterableSet(ord, list))); + public static Gen> arbSet(Ord ord, final Gen aa) { + return arbList(aa).map(list -> Set.iterableSet(ord, list)); } - public static Arbitrary> arbSet(Ord ord, final Arbitrary aa, int max) { - Gen> g = Gen.choose(0, max).bind(i -> Gen.sequenceN(i, aa.gen)).map(list -> Set.iterableSet(ord, list)); - return arbitrary(g); + public static Gen> arbSet(Ord ord, final Gen aa, int max) { + return choose(0, max).bind(i -> Gen.sequenceN(i, aa)).map(list -> Set.iterableSet(ord, list)); } @@ -830,18 +693,14 @@ public static Arbitrary> arbSet(Ord ord, final Arbitrary aa, in * @param as An arbitrary used for the throwable message. * @return An arbitrary implementation for throwables. */ - public static Arbitrary arbThrowable(final Arbitrary as) { - return arbitrary(as.gen.map(new F() { - public Throwable f(final String msg) { - return new Throwable(msg); - } - })); + public static Gen arbThrowable(final Gen as) { + return as.map(Throwable::new); } /** * An arbitrary implementation for throwables. */ - public static final Arbitrary arbThrowable = arbThrowable(arbString); + public static final Gen arbThrowable = arbThrowable(arbString); // BEGIN java.util @@ -851,58 +710,42 @@ public Throwable f(final String msg) { * @param aa An arbitrary implementation for the type over which the array list is defined. * @return An arbitrary implementation for array lists. */ - public static Arbitrary> arbArrayList(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, ArrayList>() { - public ArrayList f(final Array a) { - return new ArrayList(a.toCollection()); - } - })); + public static Gen> arbArrayList(final Gen aa) { + return arbArray(aa).map(Array::toJavaList); } /** * An arbitrary implementation for bit sets. */ - public static final Arbitrary arbBitSet = - arbitrary(arbList(arbBoolean).gen.map(new F, BitSet>() { - public BitSet f(final List bs) { - final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreachDoEffect(new Effect1>() { - public void f(final P2 bi) { - s.set(bi._2(), bi._1()); - } - }); - return s; - } - })); + public static final Gen arbBitSet = + arbList(arbBoolean).map(bs -> { + final BitSet s = new BitSet(bs.length()); + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); + return s; + }); /** * An arbitrary implementation for calendars. */ - public static final Arbitrary arbCalendar = arbitrary(arbLong.gen.map(new F() { - public Calendar f(final Long i) { - final Calendar c = Calendar.getInstance(); - c.setTimeInMillis(i); - return c; - } - })); + public static final Gen arbCalendar = arbLong.map(i -> { + final Calendar c = Calendar.getInstance(); + c.setTimeInMillis(i); + return c; + }); /** * An arbitrary implementation for dates. */ - public static final Arbitrary arbDate = arbitrary(arbLong.gen.map(new F() { - public Date f(final Long i) { - return new Date(i); - } - })); + public static final Gen arbDate = arbLong.map(Date::new); /** * Returns an arbitrary implementation for a Java enumeration. * - * @param clazz The type of enum to return an arbtrary of. + * @param clazz The type of enum to return an arbitrary of. * @return An arbitrary for instances of the supplied enum type. */ - public static > Arbitrary arbEnumValue(final Class clazz) { - return arbitrary(Gen.elements(clazz.getEnumConstants())); + public static > Gen arbEnumValue(final Class clazz) { + return elements(clazz.getEnumConstants()); } /** @@ -913,14 +756,9 @@ public static > Arbitrary arbEnumValue(final Class clazz * defined. * @return An arbitrary implementation for enum maps. */ - public static , V> Arbitrary> arbEnumMap(final Arbitrary ak, - final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, EnumMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public EnumMap f(final Hashtable ht) { - return new EnumMap(ht); - } - })); + public static , V> Gen> arbEnumMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(EnumMap::new); } /** @@ -929,25 +767,19 @@ public EnumMap f(final Hashtable ht) { * @param aa An arbitrary implementation for the type over which the enum set is defined. * @return An arbitrary implementation for enum sets. */ - public static > Arbitrary> arbEnumSet(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, EnumSet>() { - public EnumSet f(final Array a) { - return copyOf(a.toCollection()); - } - })); + public static > Gen> arbEnumSet(final Gen aa) { + return arbArray(aa).map(a -> copyOf(a.asJavaList())); } /** * An arbitrary implementation for gregorian calendars. */ - public static final Arbitrary arbGregorianCalendar = - arbitrary(arbLong.gen.map(new F() { - public GregorianCalendar f(final Long i) { - final GregorianCalendar c = new GregorianCalendar(); - c.setTimeInMillis(i); - return c; - } - })); + public static final Gen arbGregorianCalendar = + arbLong.map(i -> { + final GregorianCalendar c = new GregorianCalendar(); + c.setTimeInMillis(i); + return c; + }); /** * Returns an arbitrary implementation for hash maps. @@ -957,13 +789,8 @@ public GregorianCalendar f(final Long i) { * defined. * @return An arbitrary implementation for hash maps. */ - public static Arbitrary> arbHashMap(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, HashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public HashMap f(final Hashtable ht) { - return new HashMap(ht); - } - })); + public static Gen> arbHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(HashMap::new); } /** @@ -972,12 +799,8 @@ public HashMap f(final Hashtable ht) { * @param aa An arbitrary implementation for the type over which the hash set is defined. * @return An arbitrary implementation for hash sets. */ - public static Arbitrary> arbHashSet(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, HashSet>() { - public HashSet f(final Array a) { - return new HashSet(a.toCollection()); - } - })); + public static Gen> arbHashSet(final Gen aa) { + return arbArray(aa).map(a -> new HashSet<>(a.asJavaList())); } /** @@ -989,25 +812,14 @@ public HashSet f(final Array a) { * defined. * @return An arbitrary implementation for hash tables. */ - public static Arbitrary> arbHashtable(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbList(ak).gen.bind(arbList(av).gen, new F, F, Hashtable>>() { - public F, Hashtable> f(final List ks) { - return new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final List vs) { - final Hashtable t = new Hashtable(); - - ks.zip(vs).foreachDoEffect(new Effect1>() { - public void f(final P2 kv) { - t.put(kv._1(), kv._2()); - } - }); - - return t; - } - }; - } - })); + public static Gen> arbHashtable(final Gen ak, final Gen av) { + return arbList(ak).bind(arbList(av), ks -> vs -> { + final Hashtable t = new Hashtable<>(); + + ks.zip(vs).foreachDoEffect(kv -> t.put(kv._1(), kv._2())); + + return t; + }); } /** @@ -1019,14 +831,9 @@ public void f(final P2 kv) { * are defined. * @return An arbitrary implementation for identity hash maps. */ - public static Arbitrary> arbIdentityHashMap(final Arbitrary ak, - final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, IdentityHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public IdentityHashMap f(final Hashtable ht) { - return new IdentityHashMap(ht); - } - })); + public static Gen> arbIdentityHashMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(IdentityHashMap::new); } /** @@ -1038,13 +845,8 @@ public IdentityHashMap f(final Hashtable ht) { * defined. * @return An arbitrary implementation for linked hash maps. */ - public static Arbitrary> arbLinkedHashMap(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, LinkedHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public LinkedHashMap f(final Hashtable ht) { - return new LinkedHashMap(ht); - } - })); + public static Gen> arbLinkedHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(LinkedHashMap::new); } /** @@ -1053,12 +855,8 @@ public LinkedHashMap f(final Hashtable ht) { * @param aa An arbitrary implementation for the type over which the hash set is defined. * @return An arbitrary implementation for hash sets. */ - public static Arbitrary> arbLinkedHashSet(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, LinkedHashSet>() { - public LinkedHashSet f(final Array a) { - return new LinkedHashSet(a.toCollection()); - } - })); + public static Gen> arbLinkedHashSet(final Gen aa) { + return arbArray(aa).map(a -> new LinkedHashSet<>(a.asJavaList())); } /** @@ -1067,12 +865,8 @@ public LinkedHashSet f(final Array a) { * @param aa An arbitrary implementation for the type over which the linked list is defined. * @return An arbitrary implementation for linked lists. */ - public static Arbitrary> arbLinkedList(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, LinkedList>() { - public LinkedList f(final Array a) { - return new LinkedList(a.toCollection()); - } - })); + public static Gen> arbLinkedList(final Gen aa) { + return arbArray(aa).map(a -> new LinkedList<>(a.asJavaList())); } /** @@ -1081,30 +875,23 @@ public LinkedList f(final Array a) { * @param aa An arbitrary implementation for the type over which the priority queue is defined. * @return An arbitrary implementation for priority queues. */ - public static Arbitrary> arbPriorityQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, PriorityQueue>() { - public PriorityQueue f(final Array a) { - return new PriorityQueue(a.toCollection()); - } - })); + public static Gen> arbPriorityQueue(final Gen aa) { + return arbArray(aa).map(a -> new PriorityQueue<>(a.asJavaList())); } /** * An arbitrary implementation for properties. */ - public static final Arbitrary arbProperties = - arbitrary(arbHashtable(arbString, arbString).gen.map(new F, Properties>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Properties f(final Hashtable ht) { - final Properties p = new Properties(); - - for (final String k : ht.keySet()) { - p.setProperty(k, ht.get(k)); - } + public static final Gen arbProperties = + arbHashtable(arbString, arbString).map(ht -> { + final Properties p = new Properties(); - return p; + for (final Map.Entry entry : ht.entrySet()) { + p.setProperty(entry.getKey(), entry.getValue()); } - })); + + return p; + }); /** * Returns an arbitrary implementation for stacks. @@ -1112,14 +899,12 @@ public Properties f(final Hashtable ht) { * @param aa An arbitrary implementation for the type over which the stack is defined. * @return An arbitrary implementation for stacks. */ - public static Arbitrary> arbStack(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, Stack>() { - public Stack f(final Array a) { - final Stack s = new Stack(); - s.addAll(a.toCollection()); - return s; - } - })); + public static Gen> arbStack(final Gen aa) { + return arbArray(aa).map(a -> { + final Stack s = new Stack<>(); + s.addAll(a.asJavaList()); + return s; + }); } /** @@ -1130,50 +915,45 @@ public Stack f(final Array a) { * defined. * @return An arbitrary implementation for tree maps. */ - public static Arbitrary> arbJavaTreeMap(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, java.util.TreeMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public java.util.TreeMap f(final Hashtable ht) { - return new java.util.TreeMap(ht); - } - })); + public static Gen> arbJavaTreeMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(java.util.TreeMap::new); } /** * Returns an arbitrary implementation for tree maps. */ - public static Arbitrary> arbTreeMap(Ord ord, Arbitrary>> al) { - return arbitrary(al.gen.map(list -> fj.data.TreeMap.iterableTreeMap(ord, list))); + public static Gen> arbTreeMap(Ord ord, Gen>> al) { + return al.map(list -> fj.data.TreeMap.iterableTreeMap(ord, list)); } /** * Returns an arbitrary implementation for tree maps. */ - public static Arbitrary> arbTreeMap(Ord ord, Arbitrary ak, Arbitrary av) { + public static Gen> arbTreeMap(Ord ord, Gen ak, Gen av) { return arbTreeMap(ord, arbList(arbP2(ak, av))); } /** * Returns an arbitrary implementation for tree maps where the map size is the given arbitrary integer. */ - public static Arbitrary> arbTreeMap(Ord ord, Arbitrary ak, Arbitrary av, Arbitrary ai) { - Gen>> gl2 = ai.gen.bind(i -> { + public static Gen> arbTreeMap(Ord ord, Gen ak, Gen av, Gen ai) { + Gen>> gl2 = ai.bind(i -> { if (i < 0) { - Bottom.error("Undefined: arbitrary natural is negative (" + i + ")"); + throw Bottom.error("Undefined: arbitrary natural is negative (" + i + ")"); } - return Gen.sequenceN(Math.max(i, 0), Arbitrary.arbP2(ak, av).gen); + return Gen.sequenceN(Math.max(i, 0), arbP2(ak, av)); }); - return arbTreeMap(ord, arbitrary(gl2)); + return arbTreeMap(ord, gl2); } /** * Returns an arbitrary implementation for tree maps where the size is less than or equal to the max size. */ - public static Arbitrary> arbTreeMap(Ord ord, Arbitrary ak, Arbitrary av, int maxSize) { + public static Gen> arbTreeMap(Ord ord, Gen ak, Gen av, int maxSize) { if (maxSize < 0) { - Bottom.error("Undefined: arbitrary natural is negative (" + maxSize + ")"); + throw Bottom.error("Undefined: arbitrary natural is negative (" + maxSize + ")"); } - return arbTreeMap(ord, ak, av, arbitrary(Gen.choose(0, maxSize))); + return arbTreeMap(ord, ak, av, choose(0, maxSize)); } /** @@ -1182,12 +962,8 @@ public static Arbitrary> arbTreeMap(Ord ord, Arb * @param aa An arbitrary implementation for the type over which the tree set is defined. * @return An arbitrary implementation for tree sets. */ - public static Arbitrary> arbTreeSet(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, TreeSet>() { - public TreeSet f(final Array a) { - return new TreeSet(a.toCollection()); - } - })); + public static Gen> arbTreeSet(final Gen aa) { + return arbArray(aa).map(a -> new TreeSet<>(a.asJavaList())); } /** @@ -1196,14 +972,9 @@ public TreeSet f(final Array a) { * @param aa An arbitrary implementation for the type over which the vector is defined. * @return An arbitrary implementation for vectors. */ - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public static Arbitrary> arbVector(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, Vector>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Vector f(final Array a) { - return new Vector(a.toCollection()); - } - })); + @SuppressWarnings("UseOfObsoleteCollectionType") + public static Gen> arbVector(final Gen aa) { + return arbArray(aa).map(a -> new Vector<>(a.asJavaList())); } /** @@ -1215,13 +986,8 @@ public Vector f(final Array a) { * defined. * @return An arbitrary implementation for weak hash maps. */ - public static Arbitrary> arbWeakHashMap(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, WeakHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public WeakHashMap f(final Hashtable ht) { - return new WeakHashMap(ht); - } - })); + public static Gen> arbWeakHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(WeakHashMap::new); } // END java.util @@ -1235,22 +1001,10 @@ public WeakHashMap f(final Hashtable ht) { * defined. * @return An arbitrary implementation for array blocking queues. */ - public static Arbitrary> arbArrayBlockingQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.bind(arbInteger.gen, arbBoolean.gen, - new F, F>>>() { - public F>> f(final Array a) { - return new F>>() { - public F> f(final Integer capacity) { - return new F>() { - public ArrayBlockingQueue f(final Boolean fair) { - return new ArrayBlockingQueue(a.length() + abs(capacity), - fair, a.toCollection()); - } - }; - } - }; - } - })); + 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())); } /** @@ -1262,14 +1016,9 @@ public ArrayBlockingQueue f(final Boolean fair) { * are defined. * @return An arbitrary implementation for concurrent hash maps. */ - public static Arbitrary> arbConcurrentHashMap(final Arbitrary ak, - final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, ConcurrentHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public ConcurrentHashMap f(final Hashtable ht) { - return new ConcurrentHashMap(ht); - } - })); + public static Gen> arbConcurrentHashMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(ConcurrentHashMap::new); } /** @@ -1279,12 +1028,8 @@ public ConcurrentHashMap f(final Hashtable ht) { * defined. * @return An arbitrary implementation for concurrent linked queues. */ - public static Arbitrary> arbConcurrentLinkedQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, ConcurrentLinkedQueue>() { - public ConcurrentLinkedQueue f(final Array a) { - return new ConcurrentLinkedQueue(a.toCollection()); - } - })); + public static Gen> arbConcurrentLinkedQueue(final Gen aa) { + return arbArray(aa).map(a -> new ConcurrentLinkedQueue<>(a.asJavaList())); } /** @@ -1294,12 +1039,8 @@ public ConcurrentLinkedQueue f(final Array a) { * defined. * @return An arbitrary implementation for copy-on-write array lists. */ - public static Arbitrary> arbCopyOnWriteArrayList(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, CopyOnWriteArrayList>() { - public CopyOnWriteArrayList f(final Array a) { - return new CopyOnWriteArrayList(a.toCollection()); - } - })); + public static Gen> arbCopyOnWriteArrayList(final Gen aa) { + return arbArray(aa).map(a -> new CopyOnWriteArrayList<>(a.asJavaList())); } /** @@ -1309,12 +1050,8 @@ public CopyOnWriteArrayList f(final Array a) { * defined. * @return An arbitrary implementation for copy-on-write array sets. */ - public static Arbitrary> arbCopyOnWriteArraySet(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, CopyOnWriteArraySet>() { - public CopyOnWriteArraySet f(final Array a) { - return new CopyOnWriteArraySet(a.toCollection()); - } - })); + public static Gen> arbCopyOnWriteArraySet(final Gen aa) { + return arbArray(aa).map(a -> new CopyOnWriteArraySet<>(a.asJavaList())); } /** @@ -1323,12 +1060,8 @@ public CopyOnWriteArraySet f(final Array a) { * @param aa An arbitrary implementation for the type over which the delay queue is defined. * @return An arbitrary implementation for delay queues. */ - public static Arbitrary> arbDelayQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, DelayQueue>() { - public DelayQueue f(final Array a) { - return new DelayQueue(a.toCollection()); - } - })); + public static Gen> arbDelayQueue(final Gen aa) { + return arbArray(aa).map(a -> new DelayQueue<>(a.asJavaList())); } /** @@ -1338,12 +1071,8 @@ public DelayQueue f(final Array a) { * defined. * @return An arbitrary implementation for linked blocking queues. */ - public static Arbitrary> arbLinkedBlockingQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, LinkedBlockingQueue>() { - public LinkedBlockingQueue f(final Array a) { - return new LinkedBlockingQueue(a.toCollection()); - } - })); + public static Gen> arbLinkedBlockingQueue(final Gen aa) { + return arbArray(aa).map(a -> new LinkedBlockingQueue<>(a.asJavaList())); } /** @@ -1353,12 +1082,8 @@ public LinkedBlockingQueue f(final Array a) { * defined. * @return An arbitrary implementation for priority blocking queues. */ - public static Arbitrary> arbPriorityBlockingQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.map(new F, PriorityBlockingQueue>() { - public PriorityBlockingQueue f(final Array a) { - return new PriorityBlockingQueue(a.toCollection()); - } - })); + public static Gen> arbPriorityBlockingQueue(final Gen aa) { + return arbArray(aa).map(a -> new PriorityBlockingQueue<>(a.asJavaList())); } /** @@ -1368,18 +1093,12 @@ public PriorityBlockingQueue f(final Array a) { * defined. * @return An arbitrary implementation for priority blocking queues. */ - public static Arbitrary> arbSynchronousQueue(final Arbitrary aa) { - return arbitrary(arbArray(aa).gen.bind(arbBoolean.gen, new F, F>>() { - public F> f(final Array a) { - return new F>() { - public SynchronousQueue f(final Boolean fair) { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(a.toCollection()); - return q; - } - }; - } - })); + public static Gen> arbSynchronousQueue(final Gen aa) { + return arbArray(aa).bind(arbBoolean, a -> fair -> { + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(a.asJavaList()); + return q; + }); } // END java.util.concurrent @@ -1389,29 +1108,17 @@ public SynchronousQueue f(final Boolean fair) { /** * An arbitrary implementation for SQL dates. */ - public static final Arbitrary arbSQLDate = arbitrary(arbLong.gen.map(new F() { - public java.sql.Date f(final Long i) { - return new java.sql.Date(i); - } - })); + public static final Gen arbSQLDate = arbLong.map(java.sql.Date::new); /** * An arbitrary implementation for SQL times. */ - public static final Arbitrary Arbitrary> arbP1(final Arbitrary aa) { - return arbitrary(aa.gen.map(new F>() { - public P1 f(final A a) { - return p(a); - } - })); + public static Gen> arbP1(final Gen aa) { + return aa.map(P::p); } /** @@ -1479,16 +1177,8 @@ public P1 f(final A a) { * defined. * @return An arbitrary implementation for product-2 values. */ - public static Arbitrary> arbP2(final Arbitrary aa, final Arbitrary ab) { - return arbitrary(aa.gen.bind(ab.gen, new F>>() { - public F> f(final A a) { - return new F>() { - public P2 f(final B b) { - return p(a, b); - } - }; - } - })); + public static Gen> arbP2(final Gen aa, final Gen ab) { + return aa.bind(ab, a -> b -> p(a, b)); } /** @@ -1502,21 +1192,9 @@ public P2 f(final B b) { * defined. * @return An arbitrary implementation for product-3 values. */ - public static Arbitrary> arbP3(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, new F>>>() { - public F>> f(final A a) { - return new F>>() { - public F> f(final B b) { - return new F>() { - public P3 f(final C c) { - return p(a, b, c); - } - }; - } - }; - } - })); + public static Gen> arbP3(final Gen aa, final Gen ab, + final Gen ac) { + return aa.bind(ab, ac, a -> b -> c -> p(a, b, c)); } /** @@ -1532,25 +1210,9 @@ public P3 f(final C c) { * defined. * @return An arbitrary implementation for product-4 values. */ - public static Arbitrary> arbP4(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac, final Arbitrary ad) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, ad.gen, new F>>>>() { - public F>>> f(final A a) { - return new F>>>() { - public F>> f(final B b) { - return new F>>() { - public F> f(final C c) { - return new F>() { - public P4 f(final D d) { - return p(a, b, c, d); - } - }; - } - }; - } - }; - } - })); + public static Gen> arbP4(final Gen aa, final Gen ab, + final Gen ac, final Gen ad) { + return aa.bind(ab, ac, ad, a -> b -> c -> d -> p(a, b, c, d)); } /** @@ -1568,30 +1230,10 @@ public P4 f(final D d) { * defined. * @return An arbitrary implementation for product-5 values. */ - public static Arbitrary> arbP5(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac, final Arbitrary ad, - final Arbitrary ae) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, ad.gen, ae.gen, new F>>>>>() { - public F>>>> f(final A a) { - return new F>>>>() { - public F>>> f(final B b) { - return new F>>>() { - public F>> f(final C c) { - return new F>>() { - public F> f(final D d) { - return new F>() { - public P5 f(final E e) { - return p(a, b, c, d, e); - } - }; - } - }; - } - }; - } - }; - } - })); + public static Gen> arbP5(final Gen aa, final Gen ab, + final Gen ac, final Gen ad, + final Gen ae) { + return aa.bind(ab, ac, ad, ae, a -> b -> c -> d -> e -> p(a, b, c, d, e)); } /** @@ -1611,36 +1253,12 @@ public P5 f(final E e) { * defined. * @return An arbitrary implementation for product-6 values. */ - public static Arbitrary> arbP6(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac, final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, ad.gen, ae.gen, af.gen, - new F>>>>>>() { - public F>>>>> f(final A a) { - return new F>>>>>() { - public F>>>> f(final B b) { - return new F>>>>() { - public F>>> f(final C c) { - return new F>>>() { - public F>> f(final D d) { - return new F>>() { - public F> f(final E e) { - return new F>() { - public P6 f(final F$ f) { - return p(a, b, c, d, e, f); - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); + public static Gen> arbP6(final Gen aa, final Gen ab, + final Gen ac, final Gen ad, + final Gen ae, + final Gen af) { + return aa.bind(ab, ac, ad, ae, af, + a -> b -> c -> d -> e -> f -> p(a, b, c, d, e, f)); } /** @@ -1662,43 +1280,15 @@ public P5 f(final E e) { * defined. * @return An arbitrary implementation for product-7 values. */ - public static Arbitrary> arbP7(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, ad.gen, ae.gen, af.gen, ag.gen, - new F>>>>>>>() { - public F>>>>>> f(final A a) { - return new F>>>>>>() { - public F>>>>> f(final B b) { - return new F>>>>>() { - public F>>>> f(final C c) { - return new F>>>>() { - public F>>> f(final D d) { - return new F>>>() { - public F>> f(final E e) { - return new F>>() { - public F> f(final F$ f) { - return new F>() { - public P7 f(final G g) { - return p(a, b, c, d, e, f, g); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); + public static Gen> arbP7(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag) { + return aa.bind(ab, ac, ad, ae, af, ag, + a -> b -> c -> d -> e -> f -> g -> p(a, b, c, d, e, f, g)); } /** @@ -1722,51 +1312,16 @@ public P5 f(final E e) { * defined. * @return An arbitrary implementation for product-8 values. */ - public static Arbitrary> arbP8(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah) { - return arbitrary(aa.gen.bind(ab.gen, ac.gen, ad.gen, ae.gen, af.gen, ag.gen, ah.gen, - new F>>>>>>>>() { - public F>>>>>>> f( - final A a) { - return new F>>>>>>>() { - public F>>>>>> f( - final B b) { - return new F>>>>>>() { - public F>>>>> f( - final C c) { - return new F>>>>>() { - public F>>>> f( - final D d) { - return new F>>>>() { - public F>>> f(final E e) { - return new F>>>() { - public F>> f(final F$ f) { - return new F>>() { - public F> f(final G g) { - return new F>() { - public P8 f(final H h) { - return p(a, b, c, d, e, f, g, h); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); + public static Gen> arbP8(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah) { + return aa.bind(ab, ac, ad, ae, af, ag, ah, + a -> b -> c -> d -> e -> f -> g -> h -> p(a, b, c, d, e, f, g, h)); } + } diff --git a/quickcheck/src/main/java/fj/test/Arg.java b/quickcheck/src/main/java/fj/test/Arg.java index 337f8027..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; @@ -28,7 +27,7 @@ private Arg(final T value, final int shrinks) { * @return A new argument. */ public static Arg arg(final T value, final int shrinks) { - return new Arg(value, shrinks); + return new Arg<>(value, shrinks); } /** @@ -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/Bool.java b/quickcheck/src/main/java/fj/test/Bool.java index deb072cf..18239fa6 100644 --- a/quickcheck/src/main/java/fj/test/Bool.java +++ b/quickcheck/src/main/java/fj/test/Bool.java @@ -1,7 +1,7 @@ package fj.test; import fj.F0; -import fj.P1; + import static fj.test.Property.prop; /** diff --git a/quickcheck/src/main/java/fj/test/CheckResult.java b/quickcheck/src/main/java/fj/test/CheckResult.java index fe3ca92a..784fe4e9 100644 --- a/quickcheck/src/main/java/fj/test/CheckResult.java +++ b/quickcheck/src/main/java/fj/test/CheckResult.java @@ -5,6 +5,8 @@ import fj.Show; import fj.data.List; import fj.data.Option; +import fj.function.Strings; + import static fj.data.Option.some; import static fj.Show.listShow; import static fj.Show.showS; @@ -55,7 +57,7 @@ private CheckResult(final R r, final Option>> args, final Option>>none(), Option.none(), succeeded, discarded); + return new CheckResult(R.Passed, Option.none(), Option.none(), succeeded, discarded); } /** @@ -67,7 +69,7 @@ public static CheckResult passed(final int succeeded, final int discarded) { * @return A result that the property has been proven. */ public static CheckResult proven(final List> args, final int succeeded, final int discarded) { - return new CheckResult(R.Proven, some(args), Option.none(), succeeded, discarded); + return new CheckResult(R.Proven, some(args), Option.none(), succeeded, discarded); } /** @@ -79,7 +81,7 @@ public static CheckResult proven(final List> args, final int succeeded, f * @return A result that the property has been falsified. */ public static CheckResult falsified(final List> args, final int succeeded, final int discarded) { - return new CheckResult(R.Falsified, some(args), Option.none(), succeeded, discarded); + return new CheckResult(R.Falsified, some(args), Option.none(), succeeded, discarded); } /** @@ -90,7 +92,7 @@ public static CheckResult falsified(final List> args, final int succeeded * @return A result that the property has been exhausted in checking. */ public static CheckResult exhausted(final int succeeded, final int discarded) { - return new CheckResult(R.Exhausted, Option.>>none(), Option.none(), succeeded, discarded); + return new CheckResult(R.Exhausted, Option.none(), Option.none(), succeeded, discarded); } /** @@ -117,7 +119,7 @@ public static CheckResult propException(final List> args, final Throwable * @return A result that generating values to check the property threw an exception. */ public static CheckResult genException(final Throwable ex, final int succeeded, final int discarded) { - return new CheckResult(R.GenException, Option.>>none(), some(ex), succeeded, discarded); + return new CheckResult(R.GenException, Option.none(), some(ex), succeeded, discarded); } /** @@ -236,7 +238,7 @@ private String arguments(final CheckResult r) { return args.length() == 1 ? "argument: " + sa.showS(args.head()) : "arguments: " + listShow(sa).showS(args); } - @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public String f(final CheckResult r) { if (r.isProven()) return "OK, property proven with " + arguments(r); @@ -252,12 +254,12 @@ else if (r.isPropException()) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); r.exception().some().printStackTrace(pw); - return "Exception on property evaluation with " + arguments(r) + System.getProperty("line.separator") + sw; + return "Exception on property evaluation with " + arguments(r) + Strings.lineSeparator + sw; } else if (r.isGenException()) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); r.exception().some().printStackTrace(pw); - return "Exception on argument generation " + System.getProperty("line.separator") + sw; + return "Exception on argument generation " + Strings.lineSeparator + sw; } else throw decons(r.getClass()); } @@ -284,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/Coarbitrary.java b/quickcheck/src/main/java/fj/test/Coarbitrary.java deleted file mode 100644 index 8b778188..00000000 --- a/quickcheck/src/main/java/fj/test/Coarbitrary.java +++ /dev/null @@ -1,1190 +0,0 @@ -package fj.test; - -import fj.*; - -import static fj.Function.curry; -import static fj.P.p; - -import fj.data.*; - -import static fj.data.Array.array; -import static fj.data.List.fromString; -import static fj.data.List.nil; - -import static fj.test.Variant.variant; - -import static java.lang.Double.doubleToRawLongBits; -import static java.lang.Float.floatToRawIntBits; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.PriorityQueue; -import java.util.Properties; -import java.util.Stack; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.Vector; -import java.util.WeakHashMap; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.DelayQueue; -import java.util.concurrent.Delayed; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.SynchronousQueue; - -/** - * Transforms a type and a generator to produce a new generator. This function is used to generate - * {@link Arbitrary arbitrary} functions. - * - * @version %build.number% - */ -public abstract class Coarbitrary { - /** - * Transforms the given value and generator to a new generator with a high probability of being - * independent. - * - * @param a The value to produce the generator from. - * @param g The generator to produce the new generator from. - * @return A new generator with a high probability of being independent. - */ - public abstract Gen coarbitrary(A a, Gen g); - - - /** - * A curried version of {@link #coarbitrary(Object, Gen)}. - * - * @param a The value to produce the generator from. - * @return A curried version of {@link #coarbitrary(Object, Gen)}. - */ - public final F, Gen> coarbitrary(final A a) { - return new F, Gen>() { - public Gen f(final Gen g) { - return coarbitrary(a, g); - } - }; - } - - /** - * Composes the given function with this coarbitrary to produce a new coarbitrary. - * - * @param f The function to compose. - * @return A new coarbitrary composed with the given function. - */ - public final Coarbitrary compose(final F f) { - return new Coarbitrary() { - public Gen coarbitrary(final B b, final Gen g) { - return Coarbitrary.this.coarbitrary(f.f(b), g); - } - }; - } - - /** - * Contra-maps this coarbitrary using the given function. - * - * @param f The function to co-map with. - * @return A contra-mapped coarbitrary. - */ - public final Coarbitrary contramap(final F f) { - return new Coarbitrary() { - public Gen coarbitrary(final B b, final Gen g) { - return Coarbitrary.this.coarbitrary(f.f(b), g); - } - }; - } - - /** - * A coarbitrary for a function. - * - * @param a An arbitrary for the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function. - */ - public static Coarbitrary> coarbF(final Arbitrary a, final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F f, final Gen g) { - return a.gen.bind(new F>() { - public Gen f(final A a) { - return c.coarbitrary(f.f(a), g); - } - }); - } - }; - } - - /** - * A coarbitrary for a function-2. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-2. - */ - public static Coarbitrary> coarbF2(final Arbitrary aa, final Arbitrary ab, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F2 f, final Gen g) { - return coarbF(aa, coarbF(ab, c)).coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-3. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-3. - */ - public static Coarbitrary> coarbF3(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac, final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F3 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, c))).coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-4. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param ad An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-4. - */ - public static Coarbitrary> coarbF4(final Arbitrary aa, final Arbitrary ab, - final Arbitrary ac, final Arbitrary ad, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F4 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, coarbF(ad, c)))).coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-5. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param ad An arbitrary for part of the domain of the function. - * @param ae An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-5. - */ - public static Coarbitrary> coarbF5(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F5 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, coarbF(ad, coarbF(ae, c))))).coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-6. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param ad An arbitrary for part of the domain of the function. - * @param ae An arbitrary for part of the domain of the function. - * @param af An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-6. - */ - public static Coarbitrary> coarbF6(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F6 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, coarbF(ad, coarbF(ae, coarbF(af, c)))))).coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-7. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param ad An arbitrary for part of the domain of the function. - * @param ae An arbitrary for part of the domain of the function. - * @param af An arbitrary for part of the domain of the function. - * @param ag An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-7. - */ - public static Coarbitrary> coarbF7(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F7 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, coarbF(ad, coarbF(ae, coarbF(af, coarbF(ag, c))))))) - .coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for a function-8. - * - * @param aa An arbitrary for part of the domain of the function. - * @param ab An arbitrary for part of the domain of the function. - * @param ac An arbitrary for part of the domain of the function. - * @param ad An arbitrary for part of the domain of the function. - * @param ae An arbitrary for part of the domain of the function. - * @param af An arbitrary for part of the domain of the function. - * @param ag An arbitrary for part of the domain of the function. - * @param ah An arbitrary for part of the domain of the function. - * @param c A coarbitrary for the codomain of the function. - * @return A coarbitrary for a function-8. - */ - public static Coarbitrary> coarbF8(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah, - final Coarbitrary c) { - return new Coarbitrary>() { - public Gen coarbitrary(final F8 f, final Gen g) { - return coarbF(aa, coarbF(ab, coarbF(ac, coarbF(ad, coarbF(ae, coarbF(af, coarbF(ag, coarbF(ah, c)))))))) - .coarbitrary(curry(f), g); - } - }; - } - - /** - * A coarbitrary for booleans. - */ - public static final Coarbitrary coarbBoolean = new Coarbitrary() { - public Gen coarbitrary(final Boolean b, final Gen g) { - return variant(b ? 0 : 1, g); - } - }; - - /** - * A coarbitrary for integers. - */ - public static final Coarbitrary coarbInteger = new Coarbitrary() { - public Gen coarbitrary(final Integer i, final Gen g) { - return variant(i >= 0 ? 2 * i : -2 * i + 1, g); - } - }; - - /** - * A coarbitrary for bytes. - */ - public static final Coarbitrary coarbByte = new Coarbitrary() { - public Gen coarbitrary(final Byte b, final Gen g) { - return variant(b >= 0 ? 2 * b : -2 * b + 1, g); - } - }; - - /** - * A coarbitrary for shorts. - */ - public static final Coarbitrary coarbShort = new Coarbitrary() { - public Gen coarbitrary(final Short s, final Gen g) { - return variant(s >= 0 ? 2 * s : -2 * s + 1, g); - } - }; - - /** - * A coarbitrary for longs. - */ - public static final Coarbitrary coarbLong = new Coarbitrary() { - public Gen coarbitrary(final Long l, final Gen g) { - return variant(l >= 0L ? 2L * l : -2L * l + 1L, g); - } - }; - - /** - * A coarbitrary for characters. - */ - public static final Coarbitrary coarbCharacter = new Coarbitrary() { - public Gen coarbitrary(final Character c, final Gen g) { - return variant(c << 1, g); - } - }; - - /** - * A coarbitrary for floats. - */ - public static final Coarbitrary coarbFloat = new Coarbitrary() { - public Gen coarbitrary(final Float f, final Gen g) { - return coarbInteger.coarbitrary(floatToRawIntBits(f), g); - } - }; - - /** - * A coarbitrary for doubles. - */ - public static final Coarbitrary coarbDouble = new Coarbitrary() { - public Gen coarbitrary(final Double d, final Gen g) { - return coarbLong.coarbitrary(doubleToRawLongBits(d), g); - } - }; - - /** - * A coarbitrary for the optional value. - * - * @param ca A coarbitrary for the type of the optional value. - * @return A coarbitrary for the optional value. - */ - public static Coarbitrary> coarbOption(final Coarbitrary ca) { - return new Coarbitrary>() { - public Gen coarbitrary(final Option o, final Gen g) { - return o.isNone() ? variant(0, g) : variant(1, ca.coarbitrary(o.some(), g)); - } - }; - } - - /** - * A coarbitrary for the disjoint union. - * - * @param ca A coarbitrary for one side of the disjoint union. - * @param cb A coarbitrary for one side of the disjoint union. - * @return A coarbitrary for the disjoint union. - */ - public static Coarbitrary> coarbEither(final Coarbitrary ca, final Coarbitrary cb) { - return new Coarbitrary>() { - public Gen coarbitrary(final Either e, final Gen g) { - return e.isLeft() ? - variant(0, ca.coarbitrary(e.left().value(), g)) : - variant(1, cb.coarbitrary(e.right().value(), g)); - } - }; - } - - /** - * A coarbitrary for lists. - * - * @param ca A coarbitrary for the elements of the list. - * @return A coarbitrary for lists. - */ - public static Coarbitrary> coarbList(final Coarbitrary ca) { - return new Coarbitrary>() { - public Gen coarbitrary(final List as, final Gen g) { - return as.isEmpty() ? - variant(0, g) : - variant(1, ca.coarbitrary(as.head(), coarbitrary(as.tail(), g))); - } - }; - } - - /** - * A coarbitrary for strings. - */ - public static final Coarbitrary coarbString = new Coarbitrary() { - public Gen coarbitrary(final String s, final Gen g) { - return coarbList(coarbCharacter).coarbitrary(fromString(s), g); - } - }; - - /** - * A coarbitrary for string buffers. - */ - public static final Coarbitrary coarbStringBuffer = new Coarbitrary() { - public Gen coarbitrary(final StringBuffer s, final Gen g) { - return coarbString.coarbitrary(s.toString(), g); - } - }; - - /** - * A coarbitrary for string builders. - */ - public static final Coarbitrary coarbStringBuilder = new Coarbitrary() { - public Gen coarbitrary(final StringBuilder s, final Gen g) { - return coarbString.coarbitrary(s.toString(), g); - } - }; - - /** - * A coarbitrary for streams. - * - * @param ca A coarbitrary for the elements of the stream. - * @return A coarbitrary for streams. - */ - public static Coarbitrary> coarbStream(final Coarbitrary ca) { - return new Coarbitrary>() { - public Gen coarbitrary(final Stream as, final Gen g) { - return as.isEmpty() ? - variant(0, g) : - variant(1, ca.coarbitrary(as.head(), coarbitrary(as.tail()._1(), g))); - } - }; - } - - /** - * A coarbitrary for the provided LcgRng - * @return A coarbitrary for the provided LcgRng. - */ - public static Coarbitrary coarbLcgRng() { - return new Coarbitrary() { - @Override - public Gen coarbitrary(LcgRng rng, Gen g) { - long i = rng.getSeed(); - return variant(i >= 0 ? 2 * i : -2 * i + 1, g); - } - }; - } - - /** - * A coarbitrary for state. - */ - public static Coarbitrary> coarbState(Arbitrary as, F2 f) { - return new Coarbitrary>() { - @Override - public Gen coarbitrary(State s1, Gen g) { - return as.gen.bind(r -> { - P2 p = s1.run(r); - return variant(f.f(p._1(), p._2()), g); - }); - } - }; - } - - /** - * A coarbitrary for arrays. - * - * @param ca A coarbitrary for the elements of the array. - * @return A coarbitrary for arrays. - */ - public static Coarbitrary> coarbArray(final Coarbitrary ca) { - return new Coarbitrary>() { - public Gen coarbitrary(final Array as, final Gen g) { - return coarbList(ca).coarbitrary(as.toList(), g); - } - }; - } - - /** - * A coarbitrary for throwables. - * - * @param cs A coarbitrary for the throwable message. - * @return A coarbitrary for throwables. - */ - public static Coarbitrary coarbThrowable(final Coarbitrary cs) { - return cs.contramap(new F() { - public String f(final Throwable t) { - return t.getMessage(); - } - }); - } - - /** - * A coarbitrary for throwables. - */ - public static final Coarbitrary coarbThrowable = - coarbThrowable(coarbString); - - // BEGIN java.util - - /** - * A coarbitrary for array lists. - * - * @param ca A coarbitrary for the elements of the array list. - * @return A coarbitrary for array lists. - */ - public static Coarbitrary> coarbArrayList(final Coarbitrary ca) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final ArrayList as, final Gen g) { - return coarbArray(ca).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for bit sets. - */ - public static final Coarbitrary coarbBitSet = new Coarbitrary() { - public Gen coarbitrary(final BitSet s, final Gen g) { - List x = nil(); - - for (int i = 0; i < s.size(); i++) { - x = x.snoc(s.get(i)); - } - - return coarbList(coarbBoolean).coarbitrary(x, g); - } - }; - - /** - * A coarbitrary for calendars. - */ - public static final Coarbitrary coarbCalendar = new Coarbitrary() { - public Gen coarbitrary(final Calendar c, final Gen g) { - return coarbLong.coarbitrary(c.getTime().getTime(), g); - } - }; - - /** - * A coarbitrary for dates. - */ - public static final Coarbitrary coarbDate = new Coarbitrary() { - public Gen coarbitrary(final Date d, final Gen g) { - return coarbLong.coarbitrary(d.getTime(), g); - } - }; - - /** - * A coarbitrary for enum maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for enum maps. - */ - public static , V> Coarbitrary> coarbEnumMap(final Coarbitrary ck, - final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final EnumMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for enum sets. - * - * @param c A coarbitrary for the elements of the enum set. - * @return A coarbitrary for enum sets. - */ - public static > Coarbitrary> coarbEnumSet(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final EnumSet as, final Gen g) { - return coarbHashSet(c).coarbitrary(new HashSet(as), g); - } - }; - } - - /** - * A coarbitrary for gregorian calendars. - */ - public static final Coarbitrary coarbGregorianCalendar = new Coarbitrary() { - public Gen coarbitrary(final GregorianCalendar c, final Gen g) { - return coarbLong.coarbitrary(c.getTime().getTime(), g); - } - }; - - /** - * A coarbitrary for hash maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for hash maps. - */ - public static Coarbitrary> coarbHashMap(final Coarbitrary ck, final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final HashMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for hash sets. - * - * @param c A coarbitrary for the elements of the hash set. - * @return A coarbitrary for hash sets. - */ - public static Coarbitrary> coarbHashSet(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final HashSet as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for hash tables. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for hash tables. - */ - public static Coarbitrary> coarbHashtable(final Coarbitrary ck, final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final Hashtable h, final Gen g) { - List> x = nil(); - - for (final K k : h.keySet()) { - x = x.snoc(p(k, h.get(k))); - } - - return coarbList(coarbP2(ck, cv)).coarbitrary(x, g); - } - }; - } - - /** - * A coarbitrary for identity hash maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for identity hash maps. - */ - public static Coarbitrary> coarbIdentityHashMap(final Coarbitrary ck, - final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final IdentityHashMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for linked hash maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for linked hash maps. - */ - public static Coarbitrary> coarbLinkedHashMap(final Coarbitrary ck, - final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final LinkedHashMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for linked hash sets. - * - * @param c A coarbitrary for the elements of the linked hash set. - * @return A coarbitrary for linked hash sets. - */ - public static Coarbitrary> coarbLinkedHashSet(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final LinkedHashSet as, final Gen g) { - return coarbHashSet(c).coarbitrary(new HashSet(as), g); - } - }; - } - - /** - * A coarbitrary for linked lists. - * - * @param c A coarbitrary for the elements of the linked list. - * @return A coarbitrary for linked lists. - */ - public static Coarbitrary> coarbLinkedList(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final LinkedList as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for priority queues. - * - * @param c A coarbitrary for the elements of the priority queue. - * @return A coarbitrary for priority queues. - */ - public static Coarbitrary> coarbPriorityQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final PriorityQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for properties. - */ - public static final Coarbitrary coarbProperties = new Coarbitrary() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final Properties p, final Gen g) { - final Hashtable t = new Hashtable(); - - for (final Object s : p.keySet()) { - t.put((String) s, p.getProperty((String) s)); - } - - return coarbHashtable(coarbString, coarbString).coarbitrary(t, g); - } - }; - - /** - * A coarbitrary for stacks. - * - * @param c A coarbitrary for the elements of the stack. - * @return A coarbitrary for stacks. - */ - public static Coarbitrary> coarbStack(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final Stack as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for tree maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for tree maps. - */ - public static Coarbitrary> coarbTreeMap(final Coarbitrary ck, final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final TreeMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for tree sets. - * - * @param c A coarbitrary for the elements of the tree set. - * @return A coarbitrary for tree sets. - */ - public static Coarbitrary> coarbTreeSet(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final TreeSet as, final Gen g) { - return coarbHashSet(c).coarbitrary(new HashSet(as), g); - } - }; - } - - /** - * A coarbitrary for vectors. - * - * @param c A coarbitrary for the elements of the vector. - * @return A coarbitrary for vectors. - */ - public static Coarbitrary> coarbVector(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked", "UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final Vector as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for weak hash maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for weak hash maps. - */ - public static Coarbitrary> coarbWeakHashMap(final Coarbitrary ck, - final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final WeakHashMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - // END java.util - - // BEGIN java.util.concurrent - - /** - * A coarbitrary for array blocking queues. - * - * @param c A coarbitrary for the elements of the array blocking queue. - * @return A coarbitrary for array blocking queues. - */ - public static Coarbitrary> coarbArrayBlockingQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final ArrayBlockingQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for concurrent hash maps. - * - * @param ck A coarbitrary for the map keys. - * @param cv A coarbitrary for the map values. - * @return A coarbitrary for concurrent hash maps. - */ - public static Coarbitrary> coarbConcurrentHashMap(final Coarbitrary ck, - final Coarbitrary cv) { - return new Coarbitrary>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Gen coarbitrary(final ConcurrentHashMap m, final Gen g) { - return coarbHashtable(ck, cv).coarbitrary(new Hashtable(m), g); - } - }; - } - - /** - * A coarbitrary for concurrent linked queues. - * - * @param c A coarbitrary for the elements of the concurrent linked queue. - * @return A coarbitrary for concurrent linked queues. - */ - public static Coarbitrary> coarbConcurrentLinkedQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final ConcurrentLinkedQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for copy-on-write array lists. - * - * @param c A coarbitrary for the elements of the copy-on-write array list. - * @return A coarbitrary for copy-on-write array lists. - */ - public static Coarbitrary> coarbCopyOnWriteArrayList(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final CopyOnWriteArrayList as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for copy-on-write array sets. - * - * @param c A coarbitrary for the elements of the copy-on-write array set. - * @return A coarbitrary for copy-on-write array sets. - */ - public static Coarbitrary> coarbCopyOnWriteArraySet(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final CopyOnWriteArraySet as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for delay queues. - * - * @param c A coarbitrary for the elements of the delay queue. - * @return A coarbitrary for delay queues. - */ - public static Coarbitrary> coarbDelayQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final DelayQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for linked blocking queues. - * - * @param c A coarbitrary for the elements of the linked blocking queue. - * @return A coarbitrary for linked blocking queues. - */ - public static Coarbitrary> coarbLinkedBlockingQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final LinkedBlockingQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for priority blocking queues. - * - * @param c A coarbitrary for the elements of the priority blocking queue. - * @return A coarbitrary for priority blocking queues. - */ - public static Coarbitrary> coarbPriorityBlockingQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final PriorityBlockingQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - /** - * A coarbitrary for synchronous queues. - * - * @param c A coarbitrary for the elements of the synchronous queue. - * @return A coarbitrary for synchronous queues. - */ - public static Coarbitrary> coarbSynchronousQueue(final Coarbitrary c) { - return new Coarbitrary>() { - @SuppressWarnings({"unchecked"}) - public Gen coarbitrary(final SynchronousQueue as, final Gen g) { - return coarbArray(c).coarbitrary(array(as.toArray((A[]) new Object[as.size()])), g); - } - }; - } - - // END java.util.concurrent - - // BEGIN java.sql - - public static final Coarbitrary coarbSQLDate = new Coarbitrary() { - public Gen coarbitrary(final java.sql.Date d, final Gen g) { - return coarbLong.coarbitrary(d.getTime(), g); - } - }; - - public static final Coarbitrary coarbTimestamp = new Coarbitrary() { - public Gen coarbitrary(final Timestamp t, final Gen g) { - return coarbLong.coarbitrary(t.getTime(), g); - } - }; - - public static final Coarbitrary Coarbitrary> coarbP1(final Coarbitrary ca) { - return new Coarbitrary>() { - public Gen coarbitrary(final P1 p, final Gen g) { - return ca.coarbitrary(p._1(), g); - } - }; - } - - /** - * A coarbitrary for product-2 values. - * - * @param ca A coarbitrary for one of the types over which the product-2 is defined. - * @param cb A coarbitrary for one of the types over which the product-2 is defined. - * @return A coarbitrary for product-2 values. - */ - public static Coarbitrary> coarbP2(final Coarbitrary ca, final Coarbitrary cb) { - return new Coarbitrary>() { - public Gen coarbitrary(final P2 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), g)); - } - }; - } - - /** - * A coarbitrary for product-3 values. - * - * @param ca A coarbitrary for one of the types over which the product-3 is defined. - * @param cb A coarbitrary for one of the types over which the product-3 is defined. - * @param cc A coarbitrary for one of the types over which the product-3 is defined. - * @return A coarbitrary for product-3 values. - */ - public static Coarbitrary> coarbP3(final Coarbitrary ca, final Coarbitrary cb, - final Coarbitrary cc) { - return new Coarbitrary>() { - public Gen coarbitrary(final P3 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), cc.coarbitrary(p._3(), g))); - } - }; - } - - /** - * A coarbitrary for product-4 values. - * - * @param ca A coarbitrary for one of the types over which the product-4 is defined. - * @param cb A coarbitrary for one of the types over which the product-4 is defined. - * @param cc A coarbitrary for one of the types over which the product-4 is defined. - * @param cd A coarbitrary for one of the types over which the product-4 is defined. - * @return A coarbitrary for product-4 values. - */ - public static Coarbitrary> coarbP4(final Coarbitrary ca, final Coarbitrary cb, - final Coarbitrary cc, final Coarbitrary cd) { - return new Coarbitrary>() { - public Gen coarbitrary(final P4 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), cc.coarbitrary(p._3(), cd.coarbitrary(p._4(), g)))); - } - }; - } - - /** - * A coarbitrary for product-5 values. - * - * @param ca A coarbitrary for one of the types over which the product-5 is defined. - * @param cb A coarbitrary for one of the types over which the product-5 is defined. - * @param cc A coarbitrary for one of the types over which the product-5 is defined. - * @param cd A coarbitrary for one of the types over which the product-5 is defined. - * @param ce A coarbitrary for one of the types over which the product-5 is defined. - * @return A coarbitrary for product-5 values. - */ - public static Coarbitrary> coarbP5(final Coarbitrary ca, final Coarbitrary cb, - final Coarbitrary cc, final Coarbitrary cd, - final Coarbitrary ce) { - return new Coarbitrary>() { - public Gen coarbitrary(final P5 p, final Gen g) { - return ca.coarbitrary(p._1(), - cb.coarbitrary(p._2(), cc.coarbitrary(p._3(), cd.coarbitrary(p._4(), ce.coarbitrary(p._5(), g))))); - } - }; - } - - /** - * A coarbitrary for product-6 values. - * - * @param ca A coarbitrary for one of the types over which the product-6 is defined. - * @param cb A coarbitrary for one of the types over which the product-6 is defined. - * @param cc A coarbitrary for one of the types over which the product-6 is defined. - * @param cd A coarbitrary for one of the types over which the product-6 is defined. - * @param ce A coarbitrary for one of the types over which the product-6 is defined. - * @param cf A coarbitrary for one of the types over which the product-6 is defined. - * @return A coarbitrary for product-6 values. - */ - public static Coarbitrary> coarbP6(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf) { - return new Coarbitrary>() { - public Gen coarbitrary(final P6 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), - cc.coarbitrary(p._3(), cd.coarbitrary(p._4(), ce.coarbitrary(p._5(), cf.coarbitrary(p._6(), g)))))); - } - }; - } - - /** - * A coarbitrary for product-7 values. - * - * @param ca A coarbitrary for one of the types over which the product-7 is defined. - * @param cb A coarbitrary for one of the types over which the product-7 is defined. - * @param cc A coarbitrary for one of the types over which the product-7 is defined. - * @param cd A coarbitrary for one of the types over which the product-7 is defined. - * @param ce A coarbitrary for one of the types over which the product-7 is defined. - * @param cf A coarbitrary for one of the types over which the product-7 is defined. - * @param cg A coarbitrary for one of the types over which the product-7 is defined. - * @return A coarbitrary for product-7 values. - */ - public static Coarbitrary> coarbP7(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf, - final Coarbitrary cg) { - return new Coarbitrary>() { - public Gen coarbitrary(final P7 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), cc.coarbitrary(p._3(), - cd.coarbitrary(p._4(), ce.coarbitrary(p._5(), cf.coarbitrary(p._6(), cg.coarbitrary(p._7(), g))))))); - } - }; - } - - /** - * A coarbitrary for product-8 values. - * - * @param ca A coarbitrary for one of the types over which the product-8 is defined. - * @param cb A coarbitrary for one of the types over which the product-8 is defined. - * @param cc A coarbitrary for one of the types over which the product-8 is defined. - * @param cd A coarbitrary for one of the types over which the product-8 is defined. - * @param ce A coarbitrary for one of the types over which the product-8 is defined. - * @param cf A coarbitrary for one of the types over which the product-8 is defined. - * @param cg A coarbitrary for one of the types over which the product-8 is defined. - * @param ch A coarbitrary for one of the types over which the product-8 is defined. - * @return A coarbitrary for product-8 values. - */ - public static Coarbitrary> coarbP8(final Coarbitrary ca, - final Coarbitrary cb, - final Coarbitrary cc, - final Coarbitrary cd, - final Coarbitrary ce, - final Coarbitrary cf, - final Coarbitrary cg, - final Coarbitrary ch) { - return new Coarbitrary>() { - public Gen coarbitrary(final P8 p, final Gen g) { - return ca.coarbitrary(p._1(), cb.coarbitrary(p._2(), cc.coarbitrary(p._3(), cd.coarbitrary(p._4(), - ce.coarbitrary(p._5(), cf.coarbitrary(p._6(), cg.coarbitrary(p._7(), ch.coarbitrary(p._8(), g)))))))); - } - }; - } -} diff --git a/quickcheck/src/main/java/fj/test/Cogen.java b/quickcheck/src/main/java/fj/test/Cogen.java new file mode 100644 index 00000000..5770ea21 --- /dev/null +++ b/quickcheck/src/main/java/fj/test/Cogen.java @@ -0,0 +1,1179 @@ +package fj.test; + +import fj.*; + +import static fj.Function.curry; +import static fj.P.p; + +import fj.data.*; + +import static fj.data.Array.iterableArray; +import static fj.data.List.fromString; +import static fj.data.List.nil; + +import static fj.test.Variant.variant; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Float.floatToRawIntBits; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Calendar; +import java.util.Date; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Properties; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; +import java.util.WeakHashMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.SynchronousQueue; + +/** + * Transforms a type and a generator to produce a new generator. This function is used to generate + * {@link Gen gen} functions. + * + * @version %build.number% + */ +public abstract class Cogen { + /** + * Transforms the given value and generator to a new generator with a high probability of being + * independent. + * + * @param a The value to produce the generator from. + * @param g The generator to produce the new generator from. + * @return A new generator with a high probability of being independent. + */ + public abstract Gen cogen(A a, Gen g); + + + /** + * A curried version of {@link #cogen(Object, Gen)}. + * + * @param a The value to produce the generator from. + * @return A curried version of {@link #cogen(Object, Gen)}. + */ + public final F, Gen> cogen(final A a) { + return g -> cogen(a, g); + } + + /** + * Composes the given function with this cogen to produce a new cogen. + * + * @param f The function to compose. + * @return A new cogen composed with the given function. + */ + public final Cogen compose(final F f) { + return new Cogen() { + public Gen cogen(final B b, final Gen g) { + return Cogen.this.cogen(f.f(b), g); + } + }; + } + + /** + * Contra-maps this cogen using the given function. + * + * @param f The function to co-map with. + * @return A contra-mapped cogen. + */ + public final Cogen contramap(final F f) { + return new Cogen() { + public Gen cogen(final B b, final Gen g) { + return Cogen.this.cogen(f.f(b), g); + } + }; + } + + /** + * A cogen for a function. + * + * @param a A gen for the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function. + */ + public static Cogen> cogenF(final Gen a, final Cogen c) { + return new Cogen>() { + public Gen cogen(final F f, final Gen g) { + return a.bind(a1 -> c.cogen(f.f(a1), g)); + } + }; + } + + /** + * A cogen for a function-2. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-2. + */ + public static Cogen> cogenF2(final Gen aa, final Gen ab, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F2 f, final Gen g) { + return cogenF(aa, cogenF(ab, c)).cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-3. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-3. + */ + public static Cogen> cogenF3(final Gen aa, final Gen ab, + final Gen ac, final Cogen c) { + return new Cogen>() { + public Gen cogen(final F3 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, c))).cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-4. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param ad A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-4. + */ + public static Cogen> cogenF4(final Gen aa, final Gen ab, + final Gen ac, final Gen ad, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F4 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, cogenF(ad, c)))).cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-5. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param ad A gen for part of the domain of the function. + * @param ae A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-5. + */ + public static Cogen> cogenF5(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F5 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, cogenF(ad, cogenF(ae, c))))).cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-6. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param ad A gen for part of the domain of the function. + * @param ae A gen for part of the domain of the function. + * @param af A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-6. + */ + public static Cogen> cogenF6(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F6 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, cogenF(ad, cogenF(ae, cogenF(af, c)))))).cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-7. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param ad A gen for part of the domain of the function. + * @param ae A gen for part of the domain of the function. + * @param af A gen for part of the domain of the function. + * @param ag A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-7. + */ + public static Cogen> cogenF7(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F7 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, cogenF(ad, cogenF(ae, cogenF(af, cogenF(ag, c))))))) + .cogen(curry(f), g); + } + }; + } + + /** + * A cogen for a function-8. + * + * @param aa A gen for part of the domain of the function. + * @param ab A gen for part of the domain of the function. + * @param ac A gen for part of the domain of the function. + * @param ad A gen for part of the domain of the function. + * @param ae A gen for part of the domain of the function. + * @param af A gen for part of the domain of the function. + * @param ag A gen for part of the domain of the function. + * @param ah A gen for part of the domain of the function. + * @param c A cogen for the codomain of the function. + * @return A cogen for a function-8. + */ + public static Cogen> cogenF8(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah, + final Cogen c) { + return new Cogen>() { + public Gen cogen(final F8 f, final Gen g) { + return cogenF(aa, cogenF(ab, cogenF(ac, cogenF(ad, cogenF(ae, cogenF(af, cogenF(ag, cogenF(ah, c)))))))) + .cogen(curry(f), g); + } + }; + } + + /** + * A cogen for booleans. + */ + public static final Cogen cogenBoolean = new Cogen() { + public Gen cogen(final Boolean b, final Gen g) { + return variant(b ? 0 : 1, g); + } + }; + + /** + * A cogen for integers. + */ + public static final Cogen cogenInteger = new Cogen() { + public Gen cogen(final Integer i, final Gen g) { + return variant(i >= 0 ? 2 * i : -2 * i + 1, g); + } + }; + + /** + * A cogen for bytes. + */ + public static final Cogen cogenByte = new Cogen() { + public Gen cogen(final Byte b, final Gen g) { + return variant(b >= 0 ? 2 * b : -2 * b + 1, g); + } + }; + + /** + * A cogen for shorts. + */ + public static final Cogen cogenShort = new Cogen() { + public Gen cogen(final Short s, final Gen g) { + return variant(s >= 0 ? 2 * s : -2 * s + 1, g); + } + }; + + /** + * A cogen for longs. + */ + public static final Cogen cogenLong = new Cogen() { + public Gen cogen(final Long l, final Gen g) { + return variant(l >= 0L ? 2L * l : -2L * l + 1L, g); + } + }; + + /** + * A cogen for characters. + */ + public static final Cogen cogenCharacter = new Cogen() { + public Gen cogen(final Character c, final Gen g) { + return variant(c << 1, g); + } + }; + + /** + * A cogen for floats. + */ + public static final Cogen cogenFloat = new Cogen() { + public Gen cogen(final Float f, final Gen g) { + return cogenInteger.cogen(floatToRawIntBits(f), g); + } + }; + + /** + * A cogen for doubles. + */ + public static final Cogen cogenDouble = new Cogen() { + public Gen cogen(final Double d, final Gen g) { + return cogenLong.cogen(doubleToRawLongBits(d), g); + } + }; + + /** + * A cogen for the optional value. + * + * @param ca A cogen for the type of the optional value. + * @return A cogen for the optional value. + */ + public static Cogen> cogenOption(final Cogen ca) { + return new Cogen>() { + public Gen cogen(final Option o, final Gen g) { + return o.isNone() ? variant(0, g) : variant(1, ca.cogen(o.some(), g)); + } + }; + } + + /** + * A cogen for the disjoint union. + * + * @param ca A cogen for one side of the disjoint union. + * @param cb A cogen for one side of the disjoint union. + * @return A cogen for the disjoint union. + */ + public static Cogen> cogenEither(final Cogen ca, final Cogen cb) { + return new Cogen>() { + public Gen cogen(final Either e, final Gen g) { + return e.isLeft() ? + variant(0, ca.cogen(e.left().value(), g)) : + variant(1, cb.cogen(e.right().value(), g)); + } + }; + } + + /** + * A cogen for lists. + * + * @param ca A cogen for the elements of the list. + * @return A cogen for lists. + */ + public static Cogen> cogenList(final Cogen ca) { + return new Cogen>() { + public Gen cogen(final List as, final Gen g) { + return as.isEmpty() ? + variant(0, g) : + variant(1, ca.cogen(as.head(), cogen(as.tail(), g))); + } + }; + } + + /** + * A cogen for strings. + */ + public static final Cogen cogenString = new Cogen() { + public Gen cogen(final String s, final Gen g) { + return cogenList(cogenCharacter).cogen(fromString(s), g); + } + }; + + /** + * A cogen for string buffers. + */ + public static final Cogen cogenStringBuffer = new Cogen() { + public Gen cogen(final StringBuffer s, final Gen g) { + return cogenString.cogen(s.toString(), g); + } + }; + + /** + * A cogen for string builders. + */ + public static final Cogen cogenStringBuilder = new Cogen() { + public Gen cogen(final StringBuilder s, final Gen g) { + return cogenString.cogen(s.toString(), g); + } + }; + + /** + * A cogen for streams. + * + * @param ca A cogen for the elements of the stream. + * @return A cogen for streams. + */ + public static Cogen> cogenStream(final Cogen ca) { + return new Cogen>() { + public Gen cogen(final Stream as, final Gen g) { + return as.isEmpty() ? + variant(0, g) : + variant(1, ca.cogen(as.head(), cogen(as.tail()._1(), g))); + } + }; + } + + /** + * A cogen for the provided LcgRng + * @return A cogen for the provided LcgRng. + */ + public static Cogen cogenLcgRng() { + return new Cogen() { + @Override + public Gen cogen(LcgRng rng, Gen g) { + long i = rng.getSeed(); + return variant(i >= 0 ? 2 * i : -2 * i + 1, g); + } + }; + } + + /** + * A cogen for state. + */ + public static Cogen> cogenState(Gen as, F2 f) { + return new Cogen>() { + @Override + public Gen cogen(State s1, Gen g) { + return as.bind(r -> { + P2 p = s1.run(r); + return variant(f.f(p._1(), p._2()), g); + }); + } + }; + } + + /** + * A cogen for arrays. + * + * @param ca A cogen for the elements of the array. + * @return A cogen for arrays. + */ + public static Cogen> cogenArray(final Cogen ca) { + return new Cogen>() { + public Gen cogen(final Array as, final Gen g) { + return cogenList(ca).cogen(as.toList(), g); + } + }; + } + + /** + * A cogen for throwables. + * + * @param cs A cogen for the throwable message. + * @return A cogen for throwables. + */ + public static Cogen cogenThrowable(final Cogen cs) { + return cs.contramap(Throwable::getMessage); + } + + /** + * A cogen for throwables. + */ + public static final Cogen cogenThrowable = + cogenThrowable(cogenString); + + // BEGIN java.util + + /** + * A cogen for array lists. + * + * @param ca A cogen for the elements of the array list. + * @return A cogen for array lists. + */ + public static Cogen> cogenArrayList(final Cogen ca) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final ArrayList as, final Gen g) { + return cogenArray(ca).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for bit sets. + */ + public static final Cogen cogenBitSet = new Cogen() { + public Gen cogen(final BitSet s, final Gen g) { + List x = nil(); + + for (int i = 0; i < s.size(); i++) { + x = x.snoc(s.get(i)); + } + + return cogenList(cogenBoolean).cogen(x, g); + } + }; + + /** + * A cogen for calendars. + */ + public static final Cogen cogenCalendar = new Cogen() { + public Gen cogen(final Calendar c, final Gen g) { + return cogenLong.cogen(c.getTime().getTime(), g); + } + }; + + /** + * A cogen for dates. + */ + public static final Cogen cogenDate = new Cogen() { + public Gen cogen(final Date d, final Gen g) { + return cogenLong.cogen(d.getTime(), g); + } + }; + + /** + * A cogen for enum maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for enum maps. + */ + public static , V> Cogen> cogenEnumMap(final Cogen ck, + final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final EnumMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for enum sets. + * + * @param c A cogen for the elements of the enum set. + * @return A cogen for enum sets. + */ + public static > Cogen> cogenEnumSet(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final EnumSet as, final Gen g) { + return cogenHashSet(c).cogen(new HashSet<>(as), g); + } + }; + } + + /** + * A cogen for gregorian calendars. + */ + public static final Cogen cogenGregorianCalendar = new Cogen() { + public Gen cogen(final GregorianCalendar c, final Gen g) { + return cogenLong.cogen(c.getTime().getTime(), g); + } + }; + + /** + * A cogen for hash maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for hash maps. + */ + public static Cogen> cogenHashMap(final Cogen ck, final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final HashMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for hash sets. + * + * @param c A cogen for the elements of the hash set. + * @return A cogen for hash sets. + */ + public static Cogen> cogenHashSet(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final HashSet as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for hash tables. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for hash tables. + */ + public static Cogen> cogenHashtable(final Cogen ck, final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final Hashtable h, final Gen g) { + List> x = nil(); + + for (final Map.Entry entry : h.entrySet()) { + x = x.snoc(p(entry.getKey(), entry.getValue())); + } + + return cogenList(cogenP2(ck, cv)).cogen(x, g); + } + }; + } + + /** + * A cogen for identity hash maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for identity hash maps. + */ + public static Cogen> cogenIdentityHashMap(final Cogen ck, + final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final IdentityHashMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for linked hash maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for linked hash maps. + */ + public static Cogen> cogenLinkedHashMap(final Cogen ck, + final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final LinkedHashMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for linked hash sets. + * + * @param c A cogen for the elements of the linked hash set. + * @return A cogen for linked hash sets. + */ + public static Cogen> cogenLinkedHashSet(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final LinkedHashSet as, final Gen g) { + return cogenHashSet(c).cogen(new HashSet<>(as), g); + } + }; + } + + /** + * A cogen for linked lists. + * + * @param c A cogen for the elements of the linked list. + * @return A cogen for linked lists. + */ + public static Cogen> cogenLinkedList(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final LinkedList as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for priority queues. + * + * @param c A cogen for the elements of the priority queue. + * @return A cogen for priority queues. + */ + public static Cogen> cogenPriorityQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final PriorityQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for properties. + */ + public static final Cogen cogenProperties = new Cogen() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final Properties p, final Gen g) { + final Hashtable t = new Hashtable<>(); + + for (final Object s : p.keySet()) { + t.put((String) s, p.getProperty((String) s)); + } + + return cogenHashtable(cogenString, cogenString).cogen(t, g); + } + }; + + /** + * A cogen for stacks. + * + * @param c A cogen for the elements of the stack. + * @return A cogen for stacks. + */ + public static Cogen> cogenStack(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final Stack as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for tree maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for tree maps. + */ + public static Cogen> cogenTreeMap(final Cogen ck, final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final TreeMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for tree sets. + * + * @param c A cogen for the elements of the tree set. + * @return A cogen for tree sets. + */ + public static Cogen> cogenTreeSet(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final TreeSet as, final Gen g) { + return cogenHashSet(c).cogen(new HashSet<>(as), g); + } + }; + } + + /** + * A cogen for vectors. + * + * @param c A cogen for the elements of the vector. + * @return A cogen for vectors. + */ + public static Cogen> cogenVector(final Cogen c) { + return new Cogen>() { + @SuppressWarnings({"unchecked", "UseOfObsoleteCollectionType"}) + public Gen cogen(final Vector as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for weak hash maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for weak hash maps. + */ + public static Cogen> cogenWeakHashMap(final Cogen ck, + final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final WeakHashMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + // END java.util + + // BEGIN java.util.concurrent + + /** + * A cogen for array blocking queues. + * + * @param c A cogen for the elements of the array blocking queue. + * @return A cogen for array blocking queues. + */ + public static Cogen> cogenArrayBlockingQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final ArrayBlockingQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for concurrent hash maps. + * + * @param ck A cogen for the map keys. + * @param cv A cogen for the map values. + * @return A cogen for concurrent hash maps. + */ + public static Cogen> cogenConcurrentHashMap(final Cogen ck, + final Cogen cv) { + return new Cogen>() { + @SuppressWarnings("UseOfObsoleteCollectionType") + public Gen cogen(final ConcurrentHashMap m, final Gen g) { + return cogenHashtable(ck, cv).cogen(new Hashtable<>(m), g); + } + }; + } + + /** + * A cogen for concurrent linked queues. + * + * @param c A cogen for the elements of the concurrent linked queue. + * @return A cogen for concurrent linked queues. + */ + public static Cogen> cogenConcurrentLinkedQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final ConcurrentLinkedQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for copy-on-write array lists. + * + * @param c A cogen for the elements of the copy-on-write array list. + * @return A cogen for copy-on-write array lists. + */ + public static Cogen> cogenCopyOnWriteArrayList(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final CopyOnWriteArrayList as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for copy-on-write array sets. + * + * @param c A cogen for the elements of the copy-on-write array set. + * @return A cogen for copy-on-write array sets. + */ + public static Cogen> cogenCopyOnWriteArraySet(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final CopyOnWriteArraySet as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for delay queues. + * + * @param c A cogen for the elements of the delay queue. + * @return A cogen for delay queues. + */ + public static Cogen> cogenDelayQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final DelayQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for linked blocking queues. + * + * @param c A cogen for the elements of the linked blocking queue. + * @return A cogen for linked blocking queues. + */ + public static Cogen> cogenLinkedBlockingQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final LinkedBlockingQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for priority blocking queues. + * + * @param c A cogen for the elements of the priority blocking queue. + * @return A cogen for priority blocking queues. + */ + public static Cogen> cogenPriorityBlockingQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final PriorityBlockingQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + /** + * A cogen for synchronous queues. + * + * @param c A cogen for the elements of the synchronous queue. + * @return A cogen for synchronous queues. + */ + public static Cogen> cogenSynchronousQueue(final Cogen c) { + return new Cogen>() { + @SuppressWarnings("unchecked") + public Gen cogen(final SynchronousQueue as, final Gen g) { + return cogenArray(c).cogen(iterableArray(as), g); + } + }; + } + + // END java.util.concurrent + + // BEGIN java.sql + + public static final Cogen cogenSQLDate = new Cogen() { + public Gen cogen(final java.sql.Date d, final Gen g) { + return cogenLong.cogen(d.getTime(), g); + } + }; + + public static final Cogen cogenTimestamp = new Cogen() { + public Gen cogen(final Timestamp t, final Gen g) { + return cogenLong.cogen(t.getTime(), g); + } + }; + + public static final Cogen Cogen> cogenP1(final Cogen ca) { + return new Cogen>() { + public Gen cogen(final P1 p, final Gen g) { + return ca.cogen(p._1(), g); + } + }; + } + + /** + * A cogen for product-2 values. + * + * @param ca A cogen for one of the types over which the product-2 is defined. + * @param cb A cogen for one of the types over which the product-2 is defined. + * @return A cogen for product-2 values. + */ + public static Cogen> cogenP2(final Cogen ca, final Cogen cb) { + return new Cogen>() { + public Gen cogen(final P2 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), g)); + } + }; + } + + /** + * A cogen for product-3 values. + * + * @param ca A cogen for one of the types over which the product-3 is defined. + * @param cb A cogen for one of the types over which the product-3 is defined. + * @param cc A cogen for one of the types over which the product-3 is defined. + * @return A cogen for product-3 values. + */ + public static Cogen> cogenP3(final Cogen ca, final Cogen cb, + final Cogen cc) { + return new Cogen>() { + public Gen cogen(final P3 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), cc.cogen(p._3(), g))); + } + }; + } + + /** + * A cogen for product-4 values. + * + * @param ca A cogen for one of the types over which the product-4 is defined. + * @param cb A cogen for one of the types over which the product-4 is defined. + * @param cc A cogen for one of the types over which the product-4 is defined. + * @param cd A cogen for one of the types over which the product-4 is defined. + * @return A cogen for product-4 values. + */ + public static Cogen> cogenP4(final Cogen ca, final Cogen cb, + final Cogen cc, final Cogen cd) { + return new Cogen>() { + public Gen cogen(final P4 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), cc.cogen(p._3(), cd.cogen(p._4(), g)))); + } + }; + } + + /** + * A cogen for product-5 values. + * + * @param ca A cogen for one of the types over which the product-5 is defined. + * @param cb A cogen for one of the types over which the product-5 is defined. + * @param cc A cogen for one of the types over which the product-5 is defined. + * @param cd A cogen for one of the types over which the product-5 is defined. + * @param ce A cogen for one of the types over which the product-5 is defined. + * @return A cogen for product-5 values. + */ + public static Cogen> cogenP5(final Cogen ca, final Cogen cb, + final Cogen cc, final Cogen cd, + final Cogen ce) { + return new Cogen>() { + public Gen cogen(final P5 p, final Gen g) { + return ca.cogen(p._1(), + cb.cogen(p._2(), cc.cogen(p._3(), cd.cogen(p._4(), ce.cogen(p._5(), g))))); + } + }; + } + + /** + * A cogen for product-6 values. + * + * @param ca A cogen for one of the types over which the product-6 is defined. + * @param cb A cogen for one of the types over which the product-6 is defined. + * @param cc A cogen for one of the types over which the product-6 is defined. + * @param cd A cogen for one of the types over which the product-6 is defined. + * @param ce A cogen for one of the types over which the product-6 is defined. + * @param cf A cogen for one of the types over which the product-6 is defined. + * @return A cogen for product-6 values. + */ + public static Cogen> cogenP6(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf) { + return new Cogen>() { + public Gen cogen(final P6 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), + cc.cogen(p._3(), cd.cogen(p._4(), ce.cogen(p._5(), cf.cogen(p._6(), g)))))); + } + }; + } + + /** + * A cogen for product-7 values. + * + * @param ca A cogen for one of the types over which the product-7 is defined. + * @param cb A cogen for one of the types over which the product-7 is defined. + * @param cc A cogen for one of the types over which the product-7 is defined. + * @param cd A cogen for one of the types over which the product-7 is defined. + * @param ce A cogen for one of the types over which the product-7 is defined. + * @param cf A cogen for one of the types over which the product-7 is defined. + * @param cg A cogen for one of the types over which the product-7 is defined. + * @return A cogen for product-7 values. + */ + public static Cogen> cogenP7(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf, + final Cogen cg) { + return new Cogen>() { + public Gen cogen(final P7 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), cc.cogen(p._3(), + cd.cogen(p._4(), ce.cogen(p._5(), cf.cogen(p._6(), cg.cogen(p._7(), g))))))); + } + }; + } + + /** + * A cogen for product-8 values. + * + * @param ca A cogen for one of the types over which the product-8 is defined. + * @param cb A cogen for one of the types over which the product-8 is defined. + * @param cc A cogen for one of the types over which the product-8 is defined. + * @param cd A cogen for one of the types over which the product-8 is defined. + * @param ce A cogen for one of the types over which the product-8 is defined. + * @param cf A cogen for one of the types over which the product-8 is defined. + * @param cg A cogen for one of the types over which the product-8 is defined. + * @param ch A cogen for one of the types over which the product-8 is defined. + * @return A cogen for product-8 values. + */ + public static Cogen> cogenP8(final Cogen ca, + final Cogen cb, + final Cogen cc, + final Cogen cd, + final Cogen ce, + final Cogen cf, + final Cogen cg, + final Cogen ch) { + return new Cogen>() { + public Gen cogen(final P8 p, final Gen g) { + return ca.cogen(p._1(), cb.cogen(p._2(), cc.cogen(p._3(), cd.cogen(p._4(), + ce.cogen(p._5(), cf.cogen(p._6(), cg.cogen(p._7(), ch.cogen(p._8(), g)))))))); + } + }; + } +} diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index 9ff45dfe..8fe1e6f5 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -1,25 +1,30 @@ package fj.test; -import static fj.Bottom.error; -import fj.Effect; import fj.F; import fj.Function; -import static fj.Function.flip; -import static fj.Function.curry; +import fj.Ord; import fj.P2; -import static fj.P2.__1; import fj.Unit; -import fj.F2; -import static fj.data.Array.array; +import fj.control.Trampoline; +import fj.data.Array; import fj.data.List; -import static fj.data.List.nil; -import static fj.data.List.replicate; import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; import fj.function.Effect1; +import static fj.Bottom.error; +import static fj.Function.curry; +import static fj.Function.flip; import static fj.Monoid.intAdditionMonoid; import static fj.Ord.intOrd; - +import static fj.P2.__1; +import static fj.control.Trampoline.pure; +import static fj.control.Trampoline.suspend; +import static fj.data.Array.array; +import static fj.data.List.cons; +import static fj.data.List.nil; +import static fj.data.List.replicate; import static java.lang.Math.max; import static java.lang.Math.min; @@ -30,7 +35,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;
    @@ -42,26 +47,25 @@ 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 Arbitrary} so those can be + * class fields are of types for which there exist generators (on {@link Gen} so those can be * used to compose a generator for Person:

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

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

    -static Arbitrary<Person> personArbitrary() {
    -  final Gen<Person> personGenerator = arbInteger.gen.bind(arbString.gen, arbBoolean.gen,
    +
    {@code
    +static Gen<Person> personArbitrary() {
    +  return arbInteger.bind(arbString, arbBoolean,
           // compose the generators
           new F<Integer, F<String, F<Boolean, Person>>>() {
             public F<String, F<Boolean, Person>> f(final Integer age) {
    @@ -76,9 +80,8 @@ public Person f(final Boolean male) {
               };
             }
           });
    -  return arbitrary(personGenerator);
     }
    -
    +}
    * * @version %build.number% */ @@ -107,15 +110,7 @@ public A gen(final int i, final Rand r) { * @return A new generator after applying the mapping function. */ public Gen map(final F f) { - return new Gen(new F>() { - public F f(final Integer i) { - return new F() { - public B f(final Rand r) { - return f.f(gen(i, r)); - } - }; - } - }); + return new Gen<>(i -> r -> f.f(gen(i, r))); } /** @@ -166,15 +161,7 @@ public void foreachDoEffect(final Integer i, final Rand r, final Effect1
    f) { * @return A new generator after binding the given function. */ public Gen bind(final F> f) { - return new Gen(new F>() { - public F f(final Integer i) { - return new F() { - public B f(final Rand r) { - return f.f(gen(i, r)).f.f(i).f(r); - } - }; - } - }); + return new Gen<>(i -> r -> f.f(gen(i, r)).f.f(i).f(r)); } /** @@ -289,15 +276,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(new F, Gen>() { - public Gen f(final F f) { - return map(new F() { - public B f(final A a) { - return f.f(a); - } - }); - } - }); + return gf.bind(this::map); } /** @@ -307,15 +286,7 @@ public B f(final A a) { * @return A new generator that uses the given size. */ public Gen resize(final int s) { - return new Gen(new F>() { - public F f(final Integer i) { - return new F() { - public A f(final Rand r) { - return f.f(s).f(r); - } - }; - } - }); + return new Gen<>(i -> r -> f.f(s).f(r)); } /** @@ -325,7 +296,7 @@ public A f(final Rand r) { * @return A new generator that uses the given function. */ public static Gen gen(final F> f) { - return new Gen(f); + return new Gen<>(f); } /** @@ -349,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. @@ -357,7 +341,7 @@ public static Gen> sequenceN(final int n, final Gen g) { * @return A new generator. */ public static Gen parameterised(final F>> f) { - return new Gen(curry((i, r) -> f.f(i).f(r).gen(i, r))); + return new Gen<>(curry((i, r) -> f.f(i).f(r).gen(i, r))); } /** @@ -367,7 +351,7 @@ public static Gen parameterised(final F>> f) { * @return A new generator. */ public static Gen sized(final F> f) { - return parameterised(flip(Function.>>constant(f))); + return parameterised(flip(Function.constant(f))); } /** @@ -377,15 +361,7 @@ public static Gen sized(final F> f) { * @return A generator that always produces the given value. */ public static Gen value(final A a) { - return new Gen(new F>() { - public F f(final Integer i) { - return new F() { - public A f(final Rand r) { - return a; - } - }; - } - }); + return new Gen<>(i -> r -> a); } /** @@ -401,6 +377,12 @@ public static Gen choose(final int from, final int to) { return parameterised(curry((i, r) -> value(r.choose(f, t)))); } + public static Gen choose(final long from, final long to) { + final long f = min(from, to); + final long t = max(from, to); + return parameterised(i -> r -> value(r.choose(f, t))); + } + /** * Returns a generator that produces values between the given range (inclusive). * @@ -411,15 +393,7 @@ public static Gen choose(final int from, final int to) { public static Gen choose(final double from, final double to) { final double f = min(from, to); final double t = max(from, to); - return parameterised(new F>>() { - public F> f(final Integer i) { - return new F>() { - public Gen f(final Rand r) { - return value(r.choose(f, t)); - } - }; - } - }); + return parameterised(i -> r -> value(r.choose(f, t))); } /** @@ -428,14 +402,8 @@ public Gen f(final Rand r) { * @return A generator that never returns a value. */ public static Gen fail() { - return new Gen(new F>() { - public F f(final Integer i) { - return new F() { - public A f(final Rand r) { - throw error("Failing generator"); - } - }; - } + return new Gen<>(i -> r -> { + throw error("Failing generator"); }); } @@ -446,7 +414,7 @@ public A f(final Rand r) { * @return A new generator after joining the given generator. */ public static Gen join(final Gen> g) { - return g.bind(Function.>identity()); + return g.bind(Function.identity()); } /** @@ -460,7 +428,7 @@ public static Gen join(final Gen> g) { */ public static Gen frequency(final List>> gs) { final class Pick { - Gen pick(final int n, final List>> gs) { + Gen pick(final int n, final List>> gs) { if(gs.isEmpty()) return fail(); else { @@ -472,11 +440,7 @@ Gen pick(final int n, final List>> gs) { final F>, Integer> f = __1(); - return choose(1, intAdditionMonoid.sumLeft(gs.map(f))).bind(new F>() { - public Gen f(final Integer i) { - return new Pick().pick(i, gs); - } - }); + return choose(1, intAdditionMonoid.sumLeft(gs.map(f))).bind(i -> new Pick().pick(i, gs)); } /** @@ -487,15 +451,7 @@ public Gen f(final Integer i) { * @return A new generator that uses the given pairs of frequency and value. */ public static Gen elemFrequency(final List> as) { - return frequency(as.map(new F, P2>>() { - public P2> f(final P2 p) { - return p.map2(new F>() { - public Gen f(final A a) { - return value(a); - } - }); - } - })); + return frequency(as.map(p -> p.map2(Gen::value))); } /** @@ -506,11 +462,7 @@ public Gen f(final A a) { */ @SafeVarargs public static Gen elements(final A... as) { - return array(as).isEmpty() ? Gen.fail() : choose(0, as.length - 1).map(new F() { - public A f(final Integer i) { - return as[i]; - } - }); + return array(as).isEmpty() ? Gen.fail() : choose(0, as.length - 1).map(i -> as[i]); } /** @@ -522,11 +474,7 @@ public A f(final Integer i) { * requests. */ public static Gen oneOf(final List> gs) { - return gs.isEmpty() ? Gen.fail() : choose(0, gs.length() - 1).bind(new F>() { - public Gen f(final Integer i) { - return gs.index(i); - } - }); + return gs.isEmpty() ? Gen.fail() : choose(0, gs.length() - 1).bind(gs::index); } /** @@ -540,6 +488,10 @@ public static Gen> listOf(final Gen g, final int x) { return sized(size -> choose(x, max(x, size)).bind(n -> sequenceN(n, g))); } + public static Gen> listOfSorted(final Gen g, final int x, Ord ord) { + return listOf(g, x).map(l -> l.sort(ord)); + } + /** * Returns a generator of lists whose values come from the given generator. * @@ -561,46 +513,213 @@ public static Gen> listOf1(final Gen g) { } /** - * 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 + * 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. + * + * @param as The list from which to pick an element. + * @return A generator that picks an element from the given list. + */ + public static Gen pickOne(List as) { + // This is the fastest of the four; functionally, any of them would do + return wordOf(1, as).map(List::head); + } + + /** + * 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. + *
      + *
    • For any given selection, a generated list will always contain its elements in the same order
    • + *
    • An element will never be picked more than once
    • + *
    + *

    + * 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. * * @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. */ - public static Gen> pick(final int n, final List as) { - return n < 0 || n > as.length() ? Gen.>fail() : sequenceN(n, choose(0, as.length() - 1)).map(new F, List>() { - public List f(final List is) { - List r = nil(); + public static Gen> combinationOf(int n, List as) { + int aLength = as.length(); + return ((n >= 0) && (n <= aLength)) ? + parameterised(s -> r -> { + final class Tramp { + + // Picks elements in constant stack space + private Trampoline> tramp(List remainAs, int remainN, int remainALength) { + return suspend(() -> + (remainN == 0) ? + // We have picked N elements; stop + pure(nil()) : + // For M remaining elements of which N will be picked, pick remainAs.head() with probability N/M + (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)); + } - List iis = is.sort(intOrd); - List> aas = as.zipIndex(); + } + return value(new Tramp().tramp(as, n, aLength).run()); + }) : + fail(); + } - //noinspection ForLoopWithMissingComponent - for(; iis.isNotEmpty() && aas.isNotEmpty(); aas = aas.tail()) - if(iis.head().equals(aas.head()._2())) - iis = iis.tail(); - else - r = r.snoc(aas.head()._1()); + /** + * Returns a generator of lists that picks the given number of elements from the given list. The selection is + * a combination with replacement of elements from the given list, i.e. + *

    + *

    + * If the given number is less than zero, then the returned generator will never produce a value. Note that, + * with replacement, the given number may be larger than the length of the given list. + * + * @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. + */ + public static Gen> selectionOf(int n, List as) { + Array aArr = as.toArray(); + return (n >= 0) ? + pick(indexWord(n, aArr.length()).map(indexes -> indexes.sort(intOrd)), aArr) : + fail(); + } - return r; - } - }); + /** + * Returns a generator of lists that picks the given number of elements from the given list. The selection is + * a permutation without replacement of elements from the given list, i.e. + *

    + *

    + * 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. + * + * @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. + */ + public static Gen> permutationOf(int n, List as) { + return parameterised(s -> r -> + combinationOf(n, as).map(combination -> { + // Shuffle combination using the Fisher-Yates algorithm + Array aArr = combination.toArray(); + int length = aArr.length(); + for (int i = length - 1; i > 0; --i) { + int j = r.choose(0, i); + A tmp = aArr.get(i); + aArr.set(i, aArr.get(j)); + aArr.set(j, tmp); + } + return aArr.toList(); + })); + } + + /** + * Returns a generator of lists that picks the given number of elements from the given list. The selection is + * a permutation with replacement of elements from the given list, i.e. + *

    + *

    + * If the given number is less than zero, then the returned generator will never produce a value. Note that, + * with replacement, the given number may be larger than the length of the given list. + * + * @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. + */ + public static Gen> wordOf(int n, List as) { + Array aArr = as.toArray(); + return (n >= 0) ? + pick(indexWord(n, aArr.length()), aArr) : + fail(); + } + + private static Gen> indexWord(int n, int m) { + return sequenceN(n, choose(0, m - 1)); + } + + private static Gen> pick(Gen> indexesGen, Array as) { + return indexesGen.map(indexes -> + 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. + * 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. + *

    * * @param as The list from which to pick values. * @return A generator of lists that produces some of the values of the given list. */ - public static Gen> someOf(final List as) { - return choose(0, as.length()).bind(new F>>() { - public Gen> f(final Integer i) { - return pick(i, as); - } - }); + public static Gen> someCombinationOf(List as) { + return choose(0, as.length()).bind(n -> combinationOf(n, as)); + } + + /** + * Returns a generator of lists that produces some of the values of the given list. The selection is + * a combination with replacement of elements from the given list, i.e. + *
      + *
    • For any given selection, a generated list will always contain its elements in the same order
    • + *
    • Each element may be picked more than once
    • + *
    + * + * @param maxLength The maximum length of a generated list + * @param as The list from which to pick values. + * @return A generator of lists that produces some of the values of the given list. + */ + public static
    Gen> someSelectionOf(int maxLength, List as) { + return choose(0, maxLength).bind(n -> selectionOf(n, as)); + } + + /** + * Returns a generator of lists that produces some of the values of the given list. The selection is + * a permutation without replacement of elements from the given list, i.e. + *
      + *
    • For any given selection, a generated list may contain its elements in any order
    • + *
    • An element will never be picked more than once
    • + *
    + * + * @param as The list from which to pick values. + * @return A generator of lists that produces some of the values of the given list. + */ + public static
    Gen> somePermutationOf(List as) { + return choose(0, as.length()).bind(n -> permutationOf(n, as)); + } + + /** + * Returns a generator of lists that produces some of the values of the given list. The selection is + * a permutation with replacement of elements from the given list, i.e. + *
      + *
    • For any given selection, a generated list may contain its elements in any order
    • + *
    • Each element may be picked more than once
    • + *
    + * + * @param maxLength The maximum length of a generated list + * @param as The list from which to pick values. + * @return A generator of lists that produces some of the values of the given list. + */ + public static
    Gen> someWordOf(int maxLength, List as) { + return choose(0, maxLength).bind(n -> wordOf(n, as)); } /** @@ -610,18 +729,6 @@ public Gen> f(final Integer i) { * @return A generator for functions. */ public static Gen> promote(final F> f) { - return new Gen>(new F>>() { - public F> f(final Integer i) { - return new F>() { - public F f(final Rand r) { - return new F() { - public B f(final A a) { - return f.f(a).f.f(i).f(r); - } - }; - } - }; - } - }); + return new Gen<>(i -> r -> a -> f.f(a).f.f(i).f(r)); } } diff --git a/quickcheck/src/main/java/fj/test/Property.java b/quickcheck/src/main/java/fj/test/Property.java index 232911bd..2a327ebc 100644 --- a/quickcheck/src/main/java/fj/test/Property.java +++ b/quickcheck/src/main/java/fj/test/Property.java @@ -54,15 +54,7 @@ public Result prop(final int i, final Rand r) { * @return A generator of results from this property. */ public Gen gen() { - return Gen.gen(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - return f.f(i).f(r); - } - }; - } - }); + return Gen.gen(i -> r -> f.f(i).f(r)); } /** @@ -72,15 +64,7 @@ public Result f(final Rand r) { * @return A conjunction of this property with the given property. */ public Property and(final Property p) { - return fromGen(gen().bind(p.gen(), new F>() { - public F f(final Result res1) { - return new F() { - public Result f(final Result res2) { - return res1.isException() || res1.isFalsified() ? res1 : res2.isException() || res2.isFalsified() ? res2 : res1.isProven() || res1.isUnfalsified() ? res2 : res2.isProven() || res2.isUnfalsified() ? res1 : noResult(); - } - }; - } - })); + return fromGen(gen().bind(p.gen(), res1 -> res2 -> res1.isException() || res1.isFalsified() ? res1 : res2.isException() || res2.isFalsified() ? res2 : res1.isProven() || res1.isUnfalsified() ? res2 : res2.isProven() || res2.isUnfalsified() ? res1 : noResult())); } /** @@ -90,15 +74,7 @@ public Result f(final Result res2) { * @return A disjunction of this property with the given property. */ public Property or(final Property p) { - return fromGen(gen().bind(p.gen(), new F>() { - public F f(final Result res1) { - return new F() { - public Result f(final Result res2) { - return res1.isException() || res1.isFalsified() ? res1 : res2.isException() || res2.isFalsified() ? res2 : res1.isProven() || res1.isUnfalsified() ? res1 : res2.isProven() || res2.isUnfalsified() ? res2 : noResult(); - } - }; - } - })); + return fromGen(gen().bind(p.gen(), res1 -> res2 -> res1.isException() || res1.isFalsified() ? res1 : res2.isException() || res2.isFalsified() ? res2 : res1.isProven() || res1.isUnfalsified() ? res1 : res2.isProven() || res2.isUnfalsified() ? res2 : noResult())); } /** @@ -111,15 +87,7 @@ public Result f(final Result res2) { * @return A sequence of this property with the given property. */ public Property sequence(final Property p) { - return fromGen(gen().bind(p.gen(), new F>() { - public F f(final Result res1) { - return new F() { - public Result f(final Result res2) { - return res1.isException() || res1.isProven() || res1.isUnfalsified() ? res1 : res2.isException() || res2.isProven() || res2.isUnfalsified() ? res2 : res1.isFalsified() ? res2 : res2.isFalsified() ? res1 : noResult(); - } - }; - } - })); + return fromGen(gen().bind(p.gen(), res1 -> res2 -> res1.isException() || res1.isProven() || res1.isUnfalsified() ? res1 : res2.isException() || res2.isProven() || res2.isUnfalsified() ? res2 : res1.isFalsified() ? res2 : res2.isFalsified() ? res1 : noResult())); } /** @@ -133,7 +101,7 @@ public Result f(final Result res2) { * @param maxSize The maximum size to use for checking. * @return A result after checking this property. */ - @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public CheckResult check(final Rand r, final int minSuccessful, final int maxDiscarded, @@ -184,7 +152,7 @@ else if (x.isFalsified()) { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator} and the given + * Checks this property using a {@link Rand#standard standard random generator} and the given * arguments to produce a result. * * @param minSuccessful The minimum number of successful tests before a result is reached. @@ -226,7 +194,7 @@ public CheckResult check(final Rand r, final int minSize, final int maxSize) { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum + * Checks this property using a {@link Rand#standard standard random generator}, 100 minimum * successful checks, 500 maximum discarded tests and the given arguments to produce a result. * * @param minSize The minimum size to use for checking. @@ -239,7 +207,7 @@ public CheckResult check(final int minSize, } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum + * Checks this property using a {@link Rand#standard standard random generator}, 100 minimum * successful checks, 500 maximum discarded tests, minimum size of 0, maximum size of 100. * * @return A result after checking this property. @@ -249,7 +217,7 @@ public CheckResult check() { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, the given minimum + * Checks this property using a {@link Rand#standard standard random generator}, the given minimum * successful checks, 500 maximum discarded tests, minimum size of 0, maximum size of 100. * * @param minSuccessful The minimum number of successful tests before a result is reached. @@ -272,7 +240,7 @@ public CheckResult minSuccessful(final Rand r, final int minSuccessful) { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum + * Checks this property using a {@link Rand#standard standard random generator}, 100 minimum * successful checks, the given maximum discarded tests, minimum size of 0, maximum size of 100. * * @param maxDiscarded The maximum number of tests discarded because they did not satisfy @@ -297,7 +265,7 @@ public CheckResult maxDiscarded(final Rand r, final int maxDiscarded) { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum + * Checks this property using a {@link Rand#standard standard random generator}, 100 minimum * successful checks, 500 maximum discarded tests, the given minimum size, maximum size of 100. * * @param minSize The minimum size to use for checking. @@ -320,7 +288,7 @@ public CheckResult minSize(final Rand r, final int minSize) { } /** - * Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum + * Checks this property using a {@link Rand#standard standard random generator}, 100 minimum * successful checks, 500 maximum discarded tests, minimum size of 0, the given maximum size. * * @param maxSize The maximum size to use for checking. @@ -351,15 +319,7 @@ public CheckResult maxSize(final Rand r, final int maxSize) { * @return A property that produces a result only if the given condition satisfies. */ public static Property implies(final boolean b, final F0 p) { - return b ? p.f() : new Property(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - return noResult(); - } - }; - } - }); + return b ? p.f() : new Property(i -> r -> noResult()); } /** @@ -367,7 +327,7 @@ public Result f(final Rand r) { * will be taken from the given boolean b. */ public static Property impliesBoolean(final boolean a, final boolean b) { - return implies(a, () -> Property.prop(b)); + return implies(a, () -> prop(b)); } /** @@ -375,7 +335,7 @@ public static Property impliesBoolean(final boolean a, final boolean b) { * will be taken from the given lazy boolean b. */ public static Property impliesBoolean(final boolean a, final F0 b) { - return implies(a, () -> Property.prop(b.f())); + return implies(a, () -> prop(b.f())); } /** @@ -395,15 +355,7 @@ public static Property prop(final F> f) { * @return A property that always has the given result. */ public static Property prop(final Result r) { - return new Property(new F>() { - public F f(final Integer integer) { - return new F() { - public Result f(final Rand x) { - return r; - } - }; - } - }); + return new Property(integer -> x -> r); } /** @@ -416,7 +368,7 @@ public Result f(final Rand x) { * otherwise. */ public static Property prop(final boolean b) { - return b ? prop(Result.proven(List.>nil())) : prop(Result.falsified(List.>nil())); + return b ? prop(Result.proven(List.nil())) : prop(Result.falsified(List.nil())); } /** @@ -426,15 +378,7 @@ public static Property prop(final boolean b) { * @return A property from a generator of results. */ public static Property fromGen(final Gen g) { - return prop(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - return g.gen(i, r); - } - }; - } - }); + return prop(i -> r -> g.gen(i, r)); } /** @@ -448,60 +392,44 @@ public Result f(final Rand r) { * application of its arguments. */ public static Property forall(final Gen g, final Shrink shrink, final F> f) { - return prop(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - final class Util { - @SuppressWarnings({"IfMayBeConditional"}) - Option> first(final Stream as, final int shrinks) { - final Stream>> results = as.map(new F>>() { - public Option> f(final A a) { - final Result result = exception(f.f(a)).prop(i, r); - - return result.toOption().map(new F>() { - public P2 f(final Result result) { - return p(a, result.provenAsUnfalsified().addArg(arg(a, shrinks))); - } - }); - } - }); - - if (results.isEmpty()) - return none(); - else return results.find(new F>, Boolean>() { - public Boolean f(final Option> o) { - return failed(o); - } - }).orSome(() -> results.head()); - } - - public boolean failed(final Option> o) { - return o.isSome() && o.some()._2().failed(); - } - } - - final Util u = new Util(); - - Option> x = u.first(Stream.single(g.gen(i, r)), 0); - final F, Result> __2 = __2(); - if (u.failed(x)) { - Option or; - int shrinks = 0; - - do { - shrinks++; - or = x.map(__2); - x = u.first(shrink.shrink(x.some()._1()), shrinks); - } - while (u.failed(x)); - - return noResult(or); - } else - return noResult(x.map(__2)); - } - }; + return prop(i -> r -> { + final class Util { + @SuppressWarnings("IfMayBeConditional") + Option> first(final Stream as, final int shrinks) { + final Stream>> results = as.map(a -> { + final Result result = exception(f.f(a)).prop(i, r); + + return result.toOption().map(result1 -> p(a, result1.provenAsUnfalsified().addArg(arg(a, shrinks)))); + }); + + if (results.isEmpty()) + return none(); + else return results.find(this::failed).orSome(results::head); + } + + public boolean failed(final Option> o) { + return o.isSome() && o.some()._2().failed(); + } } + + final Util u = new Util(); + + Option> x = u.first(Stream.single(g.gen(i, r)), 0); + final F, Result> __2 = __2(); + if (u.failed(x)) { + Option or; + int shrinks = 0; + + do { + shrinks++; + or = x.map(__2); + x = u.first(shrink.shrink(x.some()._1()), shrinks); + } + while (u.failed(x)); + + return noResult(or); + } else + return noResult(x.map(__2)); }); } @@ -515,8 +443,8 @@ public boolean failed(final Option> o) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final Shrink sa, final F> f) { - return forall(aa.gen, sa, f); + public static Property propertyP(final Gen aa, final Shrink sa, final F> f) { + return forall(aa, sa, f); } /** @@ -529,7 +457,7 @@ public static Property propertyP(final Arbitrary aa, final Shrink sa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, final Shrink sa, final F f) { + public static Property property(final Gen aa, final Shrink sa, final F f) { return propertyP(aa, sa, P1.curry(f)); } @@ -542,8 +470,8 @@ public static Property property(final Arbitrary aa, final Shrink sa, f * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final F> f) { - return propertyP(aa, Shrink.empty(), f); + public static Property propertyP(final Gen aa, final F> f) { + return propertyP(aa, Shrink.empty(), f); } /** @@ -555,7 +483,7 @@ public static Property propertyP(final Arbitrary aa, final F Property property(final Arbitrary aa, final F f) { + public static Property property(final Gen aa, final F f) { return propertyP(aa, P1.curry(f)); } @@ -572,16 +500,8 @@ public static Property property(final Arbitrary aa, final F * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final Arbitrary ab, final Shrink sa, final Shrink sb, final F>> f) { - return property(aa, sa, new F() { - public Property f(final A a) { - return propertyP(ab, sb, new F>() { - public P1 f(final B b) { - return f.f(a).f(b); - } - }); - } - }); + public static Property propertyP(final Gen aa, final Gen ab, final Shrink sa, final Shrink sb, final F>> f) { + return property(aa, sa, a -> propertyP(ab, sb, b -> f.f(a).f(b))); } /** @@ -596,8 +516,8 @@ public P1 f(final B b) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, final Arbitrary ab, final Shrink sa, final Shrink sb, final F> f) { - return propertyP(aa, ab, sa, sb, compose2(P.p1(), f)); + public static Property property(final Gen aa, final Gen ab, final Shrink sa, final Shrink sb, final F> f) { + return propertyP(aa, ab, sa, sb, compose2(P.p1(), f)); } /** @@ -610,16 +530,8 @@ public static Property property(final Arbitrary aa, final Arbitrary * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final Arbitrary ab, final F>> f) { - return property(aa, new F() { - public Property f(final A a) { - return propertyP(ab, new F>() { - public P1 f(final B b) { - return f.f(a).f(b); - } - }); - } - }); + public static Property propertyP(final Gen aa, final Gen ab, final F>> f) { + return property(aa, a -> propertyP(ab, b -> f.f(a).f(b))); } /** @@ -632,8 +544,8 @@ public P1 f(final B b) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, final Arbitrary ab, final F> f) { - return propertyP(aa, ab, compose2(P.p1(), f)); + public static Property property(final Gen aa, final Gen ab, final F> f) { + return propertyP(aa, ab, compose2(P.p1(), f)); } /** @@ -648,7 +560,7 @@ public static Property property(final Arbitrary aa, final Arbitrary * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final Arbitrary ab, final Shrink sa, final Shrink sb, final F2> f) { + public static Property propertyP(final Gen aa, final Gen ab, final Shrink sa, final Shrink sb, final F2> f) { return propertyP(aa, ab, sa, sb, curry(f)); } @@ -664,8 +576,8 @@ public static Property propertyP(final Arbitrary aa, final Arbitrary Property property(final Arbitrary aa, final Arbitrary ab, final Shrink sa, final Shrink sb, final F2 f) { - return propertyP(aa, ab, sa, sb, compose2(P.p1(), curry(f))); + public static Property property(final Gen aa, final Gen ab, final Shrink sa, final Shrink sb, final F2 f) { + return propertyP(aa, ab, sa, sb, compose2(P.p1(), curry(f))); } /** @@ -678,7 +590,7 @@ public static Property property(final Arbitrary aa, final Arbitrary * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property propertyP(final Arbitrary aa, final Arbitrary ab, final F2> f) { + public static Property propertyP(final Gen aa, final Gen ab, final F2> f) { return propertyP(aa, ab, curry(f)); } @@ -692,8 +604,8 @@ public static Property propertyP(final Arbitrary aa, final Arbitrary Property property(final Arbitrary aa, final Arbitrary ab, final F2 f) { - return propertyP(aa, ab, compose2(P.p1(), curry(f))); + public static Property property(final Gen aa, final Gen ab, final F2 f) { + return propertyP(aa, ab, compose2(P.p1(), curry(f))); } /** @@ -710,26 +622,14 @@ public static Property property(final Arbitrary aa, final Arbitrary * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, final Shrink sa, final Shrink sb, final Shrink sc, final F>> f) { - return property(aa, ab, sa, sb, new F>() { - public F f(final A a) { - return new F() { - public Property f(final B b) { - return property(ac, sc, new F() { - public Property f(final C 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))); } /** @@ -743,23 +643,11 @@ public Property f(final C c) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, final F>> f) { - return property(aa, ab, new F>() { - public F f(final A a) { - return new F() { - public Property f(final B b) { - return property(ac, new F() { - public Property f(final C c) { - return f.f(a).f(b).f(c); - } - }); - } - }; - } - }); + return property(aa, ab, a -> b -> property(ac, c -> f.f(a).f(b).f(c))); } /** @@ -776,9 +664,9 @@ public Property f(final C c) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, final Shrink sa, final Shrink sb, final Shrink sc, @@ -797,9 +685,9 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, final F3 f) { return property(aa, ab, ac, curry(f)); } @@ -820,32 +708,16 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, final Shrink sa, final Shrink sb, final Shrink sc, final Shrink sd, final F>>> f) { - return property(aa, ab, ac, sa, sb, sc, new F>>() { - public F> f(final A a) { - return new F>() { - public F f(final B b) { - return new F() { - public Property f(final C c) { - return property(ad, sd, new F() { - public Property f(final D 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))); } /** @@ -860,28 +732,12 @@ public Property f(final D d) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, final F>>> f) { - return property(aa, ab, ac, new F>>() { - public F> f(final A a) { - return new F>() { - public F f(final B b) { - return new F() { - public Property f(final C c) { - return property(ad, new F() { - public Property f(final D d) { - return f.f(a).f(b).f(c).f(d); - } - }); - } - }; - } - }; - } - }); + return property(aa, ab, ac, a -> b -> c -> property(ad, d -> f.f(a).f(b).f(c).f(d))); } /** @@ -900,10 +756,10 @@ public Property f(final D d) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, final Shrink sa, final Shrink sb, final Shrink sc, @@ -924,10 +780,10 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, final F4 f) { return property(aa, ab, ac, ad, curry(f)); } @@ -950,38 +806,18 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, final Shrink sa, final Shrink sb, final Shrink sc, final Shrink sd, final Shrink se, final F>>>> f) { - return property(aa, ab, ac, ad, sa, sb, sc, sd, new F>>>() { - public F>> f(final A a) { - return new F>>() { - public F> f(final B b) { - return new F>() { - public F f(final C c) { - return new F() { - public Property f(final D d) { - return property(ae, se, new F() { - public Property f(final E 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))); } /** @@ -997,33 +833,13 @@ public Property f(final E e) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, final F>>>> f) { - return property(aa, ab, ac, ad, new F>>>() { - public F>> f(final A a) { - return new F>>() { - public F> f(final B b) { - return new F>() { - public F f(final C c) { - return new F() { - public Property f(final D d) { - return property(ae, new F() { - public Property f(final E e) { - return f.f(a).f(b).f(c).f(d).f(e); - } - }); - } - }; - } - }; - } - }; - } - }); + return property(aa, ab, ac, ad, a -> b -> c -> d -> property(ae, e -> f.f(a).f(b).f(c).f(d).f(e))); } /** @@ -1044,11 +860,11 @@ public Property f(final E e) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1071,11 +887,11 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, final F5 f) { return property(aa, ab, ac, ad, ae, curry(f)); } @@ -1100,12 +916,12 @@ public static Property property(final Arbitrary aa, * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1113,31 +929,7 @@ public static Property property(final Arbitrary aa, final Shrink se, final Shrink sf, final F>>>>> f) { - return property(aa, ab, ac, ad, ae, sa, sb, sc, sd, se, new F>>>>() { - public F>>> f(final A a) { - return new F>>>() { - public F>> f(final B b) { - return new F>>() { - public F> f(final C c) { - return new F>() { - public F f(final D d) { - return new F() { - public Property f(final E e) { - return property(af, sf, new F() { - public Property f(final F$ 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$))); } /** @@ -1154,38 +946,14 @@ public Property f(final F$ f$) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, final F>>>>> f) { - return property(aa, ab, ac, ad, ae, new F>>>>() { - public F>>> f(final A a) { - return new F>>>() { - public F>> f(final B b) { - return new F>>() { - public F> f(final C c) { - return new F>() { - public F f(final D d) { - return new F() { - public Property f(final E e) { - return property(af, new F() { - public Property f(final F$ f$) { - return f.f(a).f(b).f(c).f(d).f(e).f(f$); - } - }); - } - }; - } - }; - } - }; - } - }; - } - }); + return property(aa, ab, ac, ad, ae, a -> b -> c -> d -> e -> property(af, f$ -> f.f(a).f(b).f(c).f(d).f(e).f(f$))); } /** @@ -1208,12 +976,12 @@ public Property f(final F$ f$) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1238,12 +1006,12 @@ public Property f(final F$ f$) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, final F6 f) { return property(aa, ab, ac, ad, ae, af, curry(f)); } @@ -1270,13 +1038,13 @@ public Property f(final F$ f$) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1285,35 +1053,7 @@ public Property f(final F$ f$) { final Shrink sf, final Shrink sg, final F>>>>>> f) { - return property(aa, ab, ac, ad, ae, af, sa, sb, sc, sd, se, sf, new F>>>>>() { - public F>>>> f(final A a) { - return new F>>>>() { - public F>>> f(final B b) { - return new F>>>() { - public F>> f(final C c) { - return new F>>() { - public F> f(final D d) { - return new F>() { - public F f(final E e) { - return new F() { - public Property f(final F$ f$) { - return property(ag, sg, new F() { - public Property f(final G 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))); } /** @@ -1331,43 +1071,15 @@ public Property f(final G g) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, final F>>>>>> f) { - return property(aa, ab, ac, ad, ae, af, new F>>>>>() { - public F>>>> f(final A a) { - return new F>>>>() { - public F>>> f(final B b) { - return new F>>>() { - public F>> f(final C c) { - return new F>>() { - public F> f(final D d) { - return new F>() { - public F f(final E e) { - return new F() { - public Property f(final F$ f$) { - return property(ag, new F() { - public Property f(final G 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, a -> b -> c -> d -> e -> f$ -> property(ag, g -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g))); } /** @@ -1392,13 +1104,13 @@ public Property f(final G g) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1425,13 +1137,13 @@ public Property f(final G g) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, final F7 f) { return property(aa, ab, ac, ad, ae, af, ag, curry(f)); } @@ -1460,14 +1172,14 @@ public Property f(final G g) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1477,39 +1189,7 @@ public Property f(final G g) { 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, new F>>>>>>() { - public F>>>>> f(final A a) { - return new F>>>>>() { - public F>>>> f(final B b) { - return new F>>>>() { - public F>>> f(final C c) { - return new F>>>() { - public F>> f(final D d) { - return new F>>() { - public F> f(final E e) { - return new F>() { - public F f(final F$ f$) { - return new F() { - public Property f(final G g) { - return property(ah, sh, new F() { - public Property f(final H 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))); } /** @@ -1528,48 +1208,16 @@ public Property f(final H h) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah, final F>>>>>>> f) { - return property(aa, ab, ac, ad, ae, af, ag, new F>>>>>>() { - public F>>>>> f(final A a) { - return new F>>>>>() { - public F>>>> f(final B b) { - return new F>>>>() { - public F>>> f(final C c) { - return new F>>>() { - public F>> f(final D d) { - return new F>>() { - public F> f(final E e) { - return new F>() { - public F f(final F$ f$) { - return new F() { - public Property f(final G g) { - return property(ah, new F() { - public Property f(final H 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, a -> b -> c -> d -> e -> f$ -> g -> property(ah, h -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g).f(h))); } /** @@ -1596,14 +1244,14 @@ public Property f(final H h) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah, final Shrink sa, final Shrink sb, final Shrink sc, @@ -1632,14 +1280,14 @@ public Property f(final H h) { * @return A property where its result is derived from universal quantification across the * application of its arguments. */ - public static Property property(final Arbitrary aa, - final Arbitrary ab, - final Arbitrary ac, - final Arbitrary ad, - final Arbitrary ae, - final Arbitrary af, - final Arbitrary ag, - final Arbitrary ah, + public static Property property(final Gen aa, + final Gen ab, + final Gen ac, + final Gen ad, + final Gen ae, + final Gen af, + final Gen ag, + final Gen ah, final F8 f) { return property(aa, ab, ac, ad, ae, af, ag, ah, curry(f)); } @@ -1656,15 +1304,7 @@ public static Property exception(final F0 p) { try { return p.f(); } catch (final Throwable t) { - return new Property(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - return Result.exception(List.>nil(), t); - } - }; - } - }); + return new Property(i -> r -> Result.exception(List.nil(), t)); } } diff --git a/quickcheck/src/main/java/fj/test/Rand.java b/quickcheck/src/main/java/fj/test/Rand.java index 500d04eb..e47aa8a8 100644 --- a/quickcheck/src/main/java/fj/test/Rand.java +++ b/quickcheck/src/main/java/fj/test/Rand.java @@ -2,11 +2,12 @@ import fj.F; import fj.data.Option; -import static fj.data.Option.some; +import java.util.Random; + +import static fj.data.Option.some; import static java.lang.Math.max; import static java.lang.Math.min; -import java.util.Random; /** * A random number generator. @@ -16,10 +17,16 @@ public final class Rand { private final F, F>> f; private final F, F>> g; + private final F onReseed; + + private Rand( + F, F>> f, + F, F>> g, + F onReseed) { - private Rand(final F, F>> f, final F, F>> g) { this.f = f; this.g = g; + this.onReseed = onReseed; } /** @@ -42,9 +49,12 @@ public int choose(final long seed, final int from, final int to) { * @return A random value in the given range. */ public int choose(final int from, final int to) { - return f.f(Option.none()).f(from).f(to); + return f.f(Option.none()).f(from).f(to); } + public long choose(final long from, final long to) { + return g.f(Option.none()).f((double) from).f((double) to).longValue(); + } /** * Randomly chooses a value between the given range (inclusive). * @@ -65,7 +75,7 @@ public double choose(final long seed, final double from, final double to) { * @return A random value in the given range. */ public double choose(final double from, final double to) { - return g.f(Option.none()).f(from).f(to); + return g.f(Option.none()).f(from).f(to); } /** @@ -74,65 +84,73 @@ public double choose(final double from, final double to) { * @param seed The seed of the new random generator. * @return A random generator with the given seed. */ - public Rand reseed(final long seed) { - return new Rand(new F, F>>() { - public F> f(final Option old) { - return new F>() { - public F f(final Integer from) { - return new F() { - public Integer f(final Integer to) { - return f.f(some(seed)).f(from).f(to); - } - }; - } - }; - } - }, new F, F>>() { - public F> f(final Option old) { - return new F>() { - public F f(final Double from) { - return new F() { - public Double f(final Double to) { - return g.f(some(seed)).f(from).f(to); - } - }; - } - }; - } - }); + public Rand reseed(long seed) { + return onReseed.f(seed); } /** - * Constructs a random generator from the given functions that supply a range to produce a + * Constructs a reseedable random generator from the given functions that supply a range to produce a * result. * - * @param f The integer random generator. - * @param g The floating-point random generator. + * @param f The integer random generator. + * @param g The floating-point random generator. + * @param onReseed Function to create a reseeded Rand. * @return A random generator from the given functions that supply a range to produce a result. */ - public static Rand rand(final F, F>> f, final F, F>> g) { - return new Rand(f, g); + public static Rand rand( + F, F>> f, + F, F>> g, + F onReseed) { + + return new Rand(f, g, onReseed); } + /** + * A standard random generator that uses {@link Random}. + */ + public static final Rand standard = createStandard(new Random()); + + private static Rand createStandard(Random defaultRandom) { + return rand( + optSeed -> from -> to -> + standardChooseInt(optSeed.option(() -> defaultRandom, Random::new), from, to), + optSeed -> from -> to -> + standardChooseDbl(optSeed.option(() -> defaultRandom, Random::new), from, to), + newSeed -> createStandard(new Random(newSeed))); + } - private static final F fr = new F() { - public Random f(final Long x) { - return new Random(x); + /* + * Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (inclusive). + */ + private static int standardChooseInt(Random random, int from, int to) { + int result; + if (from != to) { + int min = min(from, to); + int max = max(from, to); + long range = (1L + max) - min; + long bound = Long.MAX_VALUE - (Long.MAX_VALUE % range); + long r = random.nextLong() & Long.MAX_VALUE; + while (r >= bound) { + // Ensure uniformity + r = random.nextLong() & Long.MAX_VALUE; + } + result = (int) ((r % range) + min); + } else { + result = from; } - }; + return result; + } - /** - * A standard random generator that uses {@link Random}. + /* + * Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (exclusive) + * + * In theory, this differs from the choose() contract, which specifies a closed interval. + * In practice, the difference shouldn't matter. */ - public static final Rand standard = new Rand(seed -> from -> to -> { - final int min = min(from, to); - final int max = max(from, to); - final Random random = seed.map(fr).orSome(new Random()); - return (int) ((random.nextLong() & Long.MAX_VALUE) % (1L + max - min)) + min; - }, seed -> from -> to -> { - final double min = min(from, to); - final double max = max(from, to); - final Random random = seed.map(fr).orSome(new Random()); - return random.nextDouble() * (max - min) + min; - }); + private static double standardChooseDbl(Random random, double from, double to) { + double min = min(from, to); + double max = max(from, to); + return ((max - min) * random.nextDouble()) + min; + } + } diff --git a/quickcheck/src/main/java/fj/test/Result.java b/quickcheck/src/main/java/fj/test/Result.java index 40a388d6..8ad743bb 100644 --- a/quickcheck/src/main/java/fj/test/Result.java +++ b/quickcheck/src/main/java/fj/test/Result.java @@ -1,7 +1,6 @@ package fj.test; import fj.F; -import fj.P1; import fj.data.List; import fj.data.Option; import static fj.data.Option.none; @@ -142,7 +141,7 @@ public Result addArg(final Arg a) { * * @return A potential result for this result. */ - @SuppressWarnings({"IfMayBeConditional"}) + @SuppressWarnings("IfMayBeConditional") public Option toOption() { if(isNoResult()) return none(); @@ -157,7 +156,7 @@ public Option toOption() { * @return The result that may be {@link #noResult() noResult()}. */ public static Result noResult(final Option r) { - return r.orSome(() -> noResult()); + return r.orSome(Result::noResult); } /** @@ -166,7 +165,7 @@ public static Result noResult(final Option r) { * @return A result representing no result. */ public static Result noResult() { - return new Result(Option.>>none(), R.NoResult, Option.none()); + return new Result(Option.none(), R.NoResult, Option.none()); } /** @@ -176,7 +175,7 @@ public static Result noResult() { * @return An unfalsified result. */ public static Result unfalsified(final List> args) { - return new Result(some(args), R.Unfalsified, Option.none()); + return new Result(some(args), R.Unfalsified, Option.none()); } /** @@ -186,7 +185,7 @@ public static Result unfalsified(final List> args) { * @return A falsified result. */ public static Result falsified(final List> args) { - return new Result(some(args), R.Falsified, Option.none()); + return new Result(some(args), R.Falsified, Option.none()); } /** @@ -196,7 +195,7 @@ public static Result falsified(final List> args) { * @return A proven result. */ public static Result proven(final List> args) { - return new Result(some(args), R.Proven, Option.none()); + return new Result(some(args), R.Proven, Option.none()); } /** diff --git a/quickcheck/src/main/java/fj/test/Shrink.java b/quickcheck/src/main/java/fj/test/Shrink.java index d1403ee7..483293e8 100644 --- a/quickcheck/src/main/java/fj/test/Shrink.java +++ b/quickcheck/src/main/java/fj/test/Shrink.java @@ -23,13 +23,14 @@ 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; import fj.data.Either; import fj.data.Java; import fj.data.List; +import fj.data.Natural; import fj.data.Option; import fj.data.Stream; @@ -37,7 +38,8 @@ 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; import java.math.BigInteger; import java.sql.Time; @@ -56,6 +58,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; +import java.util.Map; import java.util.PriorityQueue; import java.util.Properties; import java.util.Stack; @@ -119,7 +122,7 @@ public Shrink map(final F f, final F g) { * value. */ public static Shrink shrink(final F> f) { - return new Shrink(f); + return new Shrink<>(f); } /** @@ -148,7 +151,7 @@ public static Shrink empty() { * A shrink strategy for booleans using false as the bottom of the shrink. */ public static final Shrink shrinkBoolean = - shrink(Function.>constant(Stream.single(false))); + shrink(Function.constant(Stream.single(false))); /** * A shrink strategy for integers using 0 as the bottom of the shrink. @@ -180,6 +183,7 @@ public static Shrink empty() { */ public static final Shrink shrinkDouble = shrinkLong.map(Long_Double, Double_Long); + /** * Returns a shrink strategy for optional values. A 'no value' is already fully * shrunk, otherwise, the shrinking occurs on the value with the given shrink strategy. @@ -189,8 +193,8 @@ public static Shrink empty() { */ public static Shrink> shrinkOption(final Shrink sa) { return shrink(o -> o.isNone() ? - Stream.>nil() : - cons(Option.none(), () -> sa.shrink(o.some()).map(Option.some_()))); + Stream.nil() : + cons(Option.none(), () -> sa.shrink(o.some()).map(Option.some_()))); } /** @@ -202,8 +206,8 @@ public static Shrink> shrinkOption(final Shrink sa) { */ public static Shrink> shrinkEither(final Shrink sa, final Shrink sb) { return shrink(e -> e.isLeft() ? - sa.shrink(e.left().value()).map(Either.left_()) : - sb.shrink(e.right().value()).map(Either.right_())); + sa.shrink(e.left().value()).map(Either.left_()) : + sb.shrink(e.right().value()).map(Either.right_())); } /** @@ -218,7 +222,7 @@ Stream> removeChunks(final int n, final List as) { if (as.isEmpty()) return nil(); else if (as.tail().isEmpty()) - return cons(List.nil(), Stream.>nil_()); + return cons(List.nil(), Stream.nil_()); else { final int n1 = n / 2; final int n2 = n - n1; @@ -235,13 +239,13 @@ else if (as.tail().isEmpty()) .map(aas1 -> aas1.append(as2)) .interleave(removeChunks(n2, as2) .filter(isNotEmpty) - .map(aas -> as1.append(aas))))); + .map(as1::append)))); }) ); } } - @SuppressWarnings({"IfMayBeConditional"}) + @SuppressWarnings("IfMayBeConditional") Stream> shrinkOne(final List as) { if (as.isEmpty()) return nil(); @@ -263,7 +267,7 @@ Stream> shrinkOne(final List as) { * @return A shrink strategy for arrays. */ public static Shrink> shrinkArray(final Shrink sa) { - return shrinkList(sa).map(Conversions.List_Array(), Conversions.Array_List()); + return shrinkList(sa).map(Conversions.List_Array(), Conversions.Array_List()); } /** @@ -273,7 +277,7 @@ public static Shrink> shrinkArray(final Shrink sa) { * @return A shrink strategy for streams. */ public static Shrink> shrinkStream(final Shrink sa) { - return shrinkList(sa).map(Conversions.List_Stream(), Conversions.Stream_List()); + return shrinkList(sa).map(Conversions.List_Stream(), Conversions.Stream_List()); } /** @@ -301,7 +305,7 @@ public static Shrink> shrinkStream(final Shrink sa) { * @return A shrink strategy for throwables. */ public static Shrink shrinkThrowable(final Shrink ss) { - return ss.map(s -> new Throwable(s), t -> t.getMessage()); + return ss.map(Throwable::new, Throwable::getMessage); } /** @@ -318,7 +322,7 @@ public static Shrink shrinkThrowable(final Shrink ss) { * @return A shrink strategy for array lists. */ public static Shrink> shrinkArrayList(final Shrink sa) { - return shrinkList(sa).map(Java.List_ArrayList(), Java.ArrayList_List()); + return shrinkList(sa).map(Java.List_ArrayList(), Java.ArrayList_List()); } /** @@ -341,17 +345,17 @@ public static Shrink> shrinkArrayList(final Shrink sa) { * A shrink strategy for dates. */ public static final Shrink shrinkDate = - shrinkLong.map(i -> new Date(i), d -> d.getTime()); + shrinkLong.map(Date::new, Date::getTime); /** * A shrink strategy for enum maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for enum maps. */ public static , V> Shrink> shrinkEnumMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new EnumMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(EnumMap::new, Hashtable::new); } /** @@ -361,7 +365,7 @@ public static , V> Shrink> shrinkEnumMap(final S * @return A shrink strategy for enum sets. */ public static > Shrink> shrinkEnumSet(final Shrink sa) { - return shrinkList(sa).map(Java.List_EnumSet(), Java.EnumSet_List()); + return shrinkList(sa).map(Java.List_EnumSet(), Java.EnumSet_List()); } /** @@ -378,11 +382,11 @@ public static > Shrink> shrinkEnumSet(final Shrink< * A shrink strategy for hash maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for hash maps. */ public static Shrink> shrinkHashMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new HashMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(HashMap::new, Hashtable::new); } /** @@ -392,27 +396,27 @@ public static Shrink> shrinkHashMap(final Shrink sk, fin * @return A shrink strategy for hash sets. */ public static Shrink> shrinkHashSet(final Shrink sa) { - return shrinkList(sa).map(Java.List_HashSet(), Java.HashSet_List()); + return shrinkList(sa).map(Java.List_HashSet(), Java.HashSet_List()); } /** * A shrink strategy for hash tables. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for hash tables. */ - @SuppressWarnings({"UseOfObsoleteCollectionType"}) + @SuppressWarnings("UseOfObsoleteCollectionType") public static Shrink> shrinkHashtable(final Shrink sk, final Shrink sv) { return shrinkList(shrinkP2(sk, sv)).map(kvs -> { - final Hashtable h = new Hashtable(); + final Hashtable h = new Hashtable<>(); kvs.foreachDoEffect(kv -> h.put(kv._1(), kv._2())); return h; }, h -> { List> x = List.nil(); - for (final K k : h.keySet()) { - x = x.snoc(p(k, h.get(k))); + for (final Map.Entry entry : h.entrySet()) { + x = x.snoc(p(entry.getKey(), entry.getValue())); } return x; @@ -423,22 +427,22 @@ public static Shrink> shrinkHashtable(final Shrink sk, * A shrink strategy for identity hash maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for identity hash maps. */ public static Shrink> shrinkIdentityHashMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new IdentityHashMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(IdentityHashMap::new, Hashtable::new); } /** * A shrink strategy for linked hash maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for linked hash maps. */ public static Shrink> shrinkLinkedHashMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new LinkedHashMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(LinkedHashMap::new, Hashtable::new); } /** @@ -448,7 +452,7 @@ public static Shrink> shrinkLinkedHashMap(final Shrin * @return A shrink strategy for linked hash sets. */ public static Shrink> shrinkLinkedHashSet(final Shrink sa) { - return shrinkList(sa).map(Java.List_LinkedHashSet(), Java.LinkedHashSet_List()); + return shrinkList(sa).map(Java.List_LinkedHashSet(), Java.LinkedHashSet_List()); } /** @@ -458,7 +462,7 @@ public static Shrink> shrinkLinkedHashSet(final Shrink s * @return A shrink strategy for linked lists. */ public static Shrink> shrinkLinkedList(final Shrink sa) { - return shrinkList(sa).map(Java.List_LinkedList(), Java.LinkedList_List()); + return shrinkList(sa).map(Java.List_LinkedList(), Java.LinkedList_List()); } /** @@ -468,7 +472,7 @@ public static Shrink> shrinkLinkedList(final Shrink sa) { * @return A shrink strategy for priority queues. */ public static Shrink> shrinkPriorityQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_PriorityQueue(), Java.PriorityQueue_List()); + return shrinkList(sa).map(Java.List_PriorityQueue(), Java.PriorityQueue_List()); } /** @@ -478,13 +482,13 @@ public static Shrink> shrinkPriorityQueue(final Shrink s .map(h -> { final Properties p = new Properties(); - for (final String k : h.keySet()) { - p.setProperty(k, h.get(k)); + for (final Map.Entry entry : h.entrySet()) { + p.setProperty(entry.getKey(), entry.getValue()); } return p; }, p -> { - final Hashtable t = new Hashtable(); + final Hashtable t = new Hashtable<>(); for (final Object s : p.keySet()) { t.put((String) s, p.getProperty((String) s)); @@ -500,18 +504,18 @@ public static Shrink> shrinkPriorityQueue(final Shrink s * @return A shrink strategy for stacks. */ public static Shrink> shrinkStack(final Shrink sa) { - return shrinkList(sa).map(Java.List_Stack(), Java.Stack_List()); + return shrinkList(sa).map(Java.List_Stack(), Java.Stack_List()); } /** * A shrink strategy for tree maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for tree maps. */ public static Shrink> shrinkTreeMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new TreeMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(TreeMap::new, Hashtable::new); } /** @@ -521,7 +525,7 @@ public static Shrink> shrinkTreeMap(final Shrink sk, fin * @return A shrink strategy for tree sets. */ public static Shrink> shrinkTreeSet(final Shrink sa) { - return shrinkList(sa).map(Java.List_TreeSet(), Java.TreeSet_List()); + return shrinkList(sa).map(Java.List_TreeSet(), Java.TreeSet_List()); } /** @@ -531,18 +535,18 @@ public static Shrink> shrinkTreeSet(final Shrink sa) { * @return A shrink strategy for vectors. */ public static Shrink> shrinkVector(final Shrink sa) { - return shrinkList(sa).map(Java.List_Vector(), Java.Vector_List()); + return shrinkList(sa).map(Java.List_Vector(), Java.Vector_List()); } /** * A shrink strategy for weak hash maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for weak hash maps. */ public static Shrink> shrinkWeakHashMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new WeakHashMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(WeakHashMap::new, Hashtable::new); } // END java.util @@ -556,18 +560,18 @@ public static Shrink> shrinkWeakHashMap(final Shrink * @return A shrink strategy for array blocking queues. */ public static Shrink> shrinkArrayBlockingQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_ArrayBlockingQueue(false), Java.ArrayBlockingQueue_List()); + return shrinkList(sa).map(Java.List_ArrayBlockingQueue(false), Java.ArrayBlockingQueue_List()); } /** * A shrink strategy for concurrent hash maps. * * @param sk The shrink strategy for keys. - * @param sv The shrink stratgey for values. + * @param sv The shrink strategy for values. * @return A shrink strategy for concurrent hash maps. */ public static Shrink> shrinkConcurrentHashMap(final Shrink sk, final Shrink sv) { - return shrinkHashtable(sk, sv).map(h -> new ConcurrentHashMap(h), m -> new Hashtable(m)); + return shrinkHashtable(sk, sv).map(ConcurrentHashMap::new, Hashtable::new); } /** @@ -577,7 +581,7 @@ public static Shrink> shrinkConcurrentHashMap(fin * @return A shrink strategy for concurrent linked queues. */ public static Shrink> shrinkConcurrentLinkedQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_ConcurrentLinkedQueue(), Java.ConcurrentLinkedQueue_List()); + return shrinkList(sa).map(Java.List_ConcurrentLinkedQueue(), Java.ConcurrentLinkedQueue_List()); } /** @@ -587,7 +591,7 @@ public static Shrink> shrinkConcurrentLinkedQueue(f * @return A shrink strategy for copy on write array lists. */ public static Shrink> shrinkCopyOnWriteArrayList(final Shrink sa) { - return shrinkList(sa).map(Java.List_CopyOnWriteArrayList(), Java.CopyOnWriteArrayList_List()); + return shrinkList(sa).map(Java.List_CopyOnWriteArrayList(), Java.CopyOnWriteArrayList_List()); } /** @@ -597,7 +601,7 @@ public static Shrink> shrinkCopyOnWriteArrayList(fin * @return A shrink strategy for copy on write array sets. */ public static Shrink> shrinkCopyOnWriteArraySet(final Shrink sa) { - return shrinkList(sa).map(Java.List_CopyOnWriteArraySet(), Java.CopyOnWriteArraySet_List()); + return shrinkList(sa).map(Java.List_CopyOnWriteArraySet(), Java.CopyOnWriteArraySet_List()); } /** @@ -607,7 +611,7 @@ public static Shrink> shrinkCopyOnWriteArraySet(final * @return A shrink strategy for delay queues. */ public static Shrink> shrinkDelayQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_DelayQueue(), Java.DelayQueue_List()); + return shrinkList(sa).map(Java.List_DelayQueue(), Java.DelayQueue_List()); } /** @@ -617,7 +621,7 @@ public static Shrink> shrinkDelayQueue(final S * @return A shrink strategy for linked blocking queues. */ public static Shrink> shrinkLinkedBlockingQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_LinkedBlockingQueue(), Java.LinkedBlockingQueue_List()); + return shrinkList(sa).map(Java.List_LinkedBlockingQueue(), Java.LinkedBlockingQueue_List()); } /** @@ -627,7 +631,7 @@ public static Shrink> shrinkLinkedBlockingQueue(final * @return A shrink strategy for priority blocking queues. */ public static Shrink> shrinkPriorityBlockingQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_PriorityBlockingQueue(), Java.PriorityBlockingQueue_List()); + return shrinkList(sa).map(Java.List_PriorityBlockingQueue(), Java.PriorityBlockingQueue_List()); } /** @@ -637,7 +641,7 @@ public static Shrink> shrinkPriorityBlockingQueue(f * @return A shrink strategy for synchronous queues. */ public static Shrink> shrinkSynchronousQueue(final Shrink sa) { - return shrinkList(sa).map(Java.List_SynchronousQueue(false), Java.SynchronousQueue_List()); + return shrinkList(sa).map(Java.List_SynchronousQueue(false), Java.SynchronousQueue_List()); } // END java.util.concurrent @@ -648,19 +652,19 @@ public static Shrink> shrinkSynchronousQueue(final Shrin * A shrink strategy for SQL dates. */ public static final Shrink shrinkSQLDate = - shrinkLong.map(i -> new java.sql.Date(i), c -> c.getTime()); + shrinkLong.map(java.sql.Date::new, Date::getTime); /** * A shrink strategy for SQL times. */ public static final Shrink Shrink> shrinkSynchronousQueue(final Shrin /** * A shrink strategy for big integers. */ - public static final Shrink shrinkBigInteger = - shrinkP2(shrinkByte, shrinkArray(shrinkByte)).map(bs -> { - final byte[] x = new byte[bs._2().length() + 1]; - - for (int i = 0; i < bs._2().array().length; i++) { - x[i] = bs._2().get(i); - } - - x[bs._2().length()] = bs._1(); + public static final Shrink shrinkBigInteger = shrink(i -> { + final Equal eq = Equal.bigintEqual; + final BigInteger two = BigInteger.valueOf(2L); + if (eq.eq(i, ZERO)) { + return nil(); + } else { + final Stream is = cons(ZERO, + () -> iterate(x -> x.divide(two), i) + .takeWhile(x2 -> eq.notEq(x2, ZERO)) + .map(x2 -> i.subtract(x2))); - return new BigInteger(x); - }, i -> { - final byte[] b = i.toByteArray(); - final Byte[] x = new Byte[b.length - 1]; - arraycopy(b, 0, x, 0, b.length - 1); - return p(b[0], array(x)); - }); + return Ord.bigintOrd.isLessThan(i, ZERO) ? cons(i.negate(), () -> is) : is; + } + }); /** * A shrink strategy for big decimals. */ public static final Shrink shrinkBigDecimal = - shrinkBigInteger.map(i -> new BigDecimal(i), d -> d.toBigInteger()); + shrinkBigInteger.map(BigDecimal::new, BigDecimal::toBigInteger); // END java.math + /** + * A shrink strategy for naturals. + */ + public static final Shrink shrinkNatural = shrinkBigInteger.map(l -> Natural.natural(l).orSome(Natural.ZERO), Natural::bigIntegerValue); + + /** * Returns a shrinking strategy for product-1 values. * @@ -702,7 +709,7 @@ public static Shrink> shrinkSynchronousQueue(final Shrin * @return a shrinking strategy for product-1 values. */ public static Shrink> shrinkP1(final Shrink sa) { - return shrink(p -> sa.shrink(p._1()).map(a -> p(a))); + return shrink(p -> sa.shrink(p._1()).map(P::p)); } /** @@ -845,4 +852,5 @@ public static Shrink> shrinkP5(final Shrink sg.shrink(p._7()), sh.shrink(p._8()), p8); }); } + } diff --git a/quickcheck/src/main/java/fj/test/Variant.java b/quickcheck/src/main/java/fj/test/Variant.java index f50bf923..a0ef3276 100644 --- a/quickcheck/src/main/java/fj/test/Variant.java +++ b/quickcheck/src/main/java/fj/test/Variant.java @@ -11,7 +11,7 @@ * @version %build.number% */ public final class Variant { - private static final HashMap> variantMemo = new HashMap>(); + private static final HashMap> variantMemo = new HashMap<>(); private static final class LongGen { private final long n; diff --git a/quickcheck/src/main/java/fj/test/reflect/Check.java b/quickcheck/src/main/java/fj/test/reflect/Check.java index 199f399a..a1eea79b 100644 --- a/quickcheck/src/main/java/fj/test/reflect/Check.java +++ b/quickcheck/src/main/java/fj/test/reflect/Check.java @@ -1,7 +1,5 @@ package fj.test.reflect; -import static fj.Bottom.error; -import fj.Class; import static fj.Class.clas; import fj.F; import fj.Function; @@ -25,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; /** @@ -81,11 +78,7 @@ public static List> check(final List List> check(final List> c, final Rand r, final String... categories) { - return join(c.map(new F, List>>() { - public List> f(final java.lang.Class c) { - return check(c, r, categories); - } - })); + return join(c.map(c1 -> check(c1, r, categories))); } /** @@ -143,18 +136,12 @@ public static List> check(final java.lang.Class c * @return The results of checking the properties on the given class. */ public static List> check(final java.lang.Class c, final Rand r, final String... categories) { - return join(clas(c).inheritance().map(new F, List>>>() { - public List>> f(final Class c) { - return properties(c.clas(), categories); - } - })).map(new F>, P2>() { - public P2 f(final P3> p) { - if(p._3().isSome()) { - final CheckParams ps = p._3().some(); - return p(p._2(), p._1().check(r, ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize())); - } else - return p(p._2(), p._1().check(r)); - } + return join(clas(c).inheritance().map(c1 -> properties(c1.clas(), categories))).map(p -> { + if(p._3().isSome()) { + final CheckParams ps = p._3().some(); + return p(p._2(), p._1().check(r, ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize())); + } else + return p(p._2(), p._1().check(r)); }); } @@ -188,68 +175,62 @@ 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(new F() { - public PropertyMember f(final Field f) { - return new PropertyMember() { - public java.lang.Class type() { - return f.getType(); - } - - public AnnotatedElement element() { - return f; - } - - public String name() { - return f.getName(); - } - - public int modifiers() { - return f.getModifiers(); - } - - public Property invoke(final X x) throws IllegalAccessException { - f.setAccessible(true); - return (Property)f.get(x); - } - - public boolean isProperty() { - return true; - } - }; + final Array>> propFields = properties(array(c.getDeclaredFields()).map(f -> new PropertyMember() { + public java.lang.Class type() { + return f.getType(); + } + + public AnnotatedElement element() { + return f; + } + + public String name() { + return f.getName(); + } + + public int modifiers() { + return f.getModifiers(); + } + + public Property invoke(final X x) throws IllegalAccessException { + f.setAccessible(true); + return (Property)f.get(x); + } + + public boolean isProperty() { + return true; } }), c, categories); //noinspection ClassEscapesDefinedScope - final Array>> propMethods = properties(array(c.getDeclaredMethods()).map(new F() { - public PropertyMember f(final Method m) { - //noinspection ProhibitedExceptionDeclared - return new PropertyMember() { - public java.lang.Class type() { - return m.getReturnType(); - } - - public AnnotatedElement element() { - return m; - } - - public String name() { - return m.getName(); - } - - public int modifiers() { - return m.getModifiers(); - } - - public Property invoke(final X x) throws Exception { - m.setAccessible(true); - return (Property)m.invoke(x); - } - - public boolean isProperty() { - return m.getParameterTypes().length == 0; - } - }; - } + final Array>> propMethods = properties(array(c.getDeclaredMethods()).map(m -> { + //noinspection ProhibitedExceptionDeclared + return new PropertyMember() { + public java.lang.Class type() { + return m.getReturnType(); + } + + public AnnotatedElement element() { + return m; + } + + public String name() { + return m.getName(); + } + + public int modifiers() { + return m.getModifiers(); + } + + public Property invoke(final X x) throws Exception { + m.setAccessible(true); + return (Property)m.invoke(x); + } + + public boolean isProperty() { + return m.getParameterTypes().length == 0; + } + }; }), c, categories); return propFields.append(propMethods).toList(); @@ -260,72 +241,47 @@ private interface PropertyMember { AnnotatedElement element(); String name(); int modifiers(); - @SuppressWarnings({"ProhibitedExceptionDeclared"}) + @SuppressWarnings("ProhibitedExceptionDeclared") Property invoke(X x) throws Exception; boolean isProperty(); } private static Array>> properties(final Array ms, final java.lang.Class declaringClass, final String... categories) { - final Option t = emptyCtor(declaringClass).map(new F, T>() { - @SuppressWarnings({"OverlyBroadCatchBlock"}) - public T f(final Constructor ctor) { - try { - ctor.setAccessible(true); - return ctor.newInstance(); - } catch(Exception e) { - throw new Error(e.getMessage(), e); - } + final Option t = emptyCtor(declaringClass).map(ctor -> { + try { + ctor.setAccessible(true); + return ctor.newInstance(); + } catch(Exception e) { + throw new Error(e.getMessage(), e); } }); - final F> p = new F>() { - public F f(final AnnotatedElement e) { - return new F() { - public Boolean f(final String s) { - final F p = new F() { - public Boolean f(final Category c) { - return array(c.value()).exists(new F() { - public Boolean f(final String cs) { - return cs.equals(s); - } - }); - } - }; - - @SuppressWarnings("unchecked") - final List bss = somes(list(fromNull(e.getAnnotation(Category.class)).map(p), - fromNull(declaringClass.getAnnotation(Category.class)).map(p))); - return bss.exists(Function.identity()); - } - }; - } - }; + final F> p = e -> s -> { + final F p1 = c -> array(c.value()).exists(cs -> cs.equals(s)); - final F nameS = new F() { - public String f(final Name name) { - return name.value(); - } + @SuppressWarnings("unchecked") + final List bss = somes(list(fromNull(e.getAnnotation(Category.class)).map(p1), + fromNull(declaringClass.getAnnotation(Category.class)).map(p1))); + return bss.exists(Function.identity()); }; - return ms.filter(new F() { - public Boolean f(final PropertyMember m) { - //noinspection ObjectEquality - return m.isProperty() && - m.type() == Property.class && - !m.element().isAnnotationPresent(NoCheck.class) && - !declaringClass.isAnnotationPresent(NoCheck.class) && - (categories.length == 0 || array(categories).exists(p.f(m.element()))) && - (t.isSome() || isStatic(m.modifiers())); - } - }).map(new F>>() { - public P3> f(final PropertyMember m) { - try { - final Option params = fromNull(m.element().getAnnotation(CheckParams.class)).orElse(fromNull(declaringClass.getAnnotation(CheckParams.class))); - final String name = fromNull(m.element().getAnnotation(Name.class)).map(nameS).orSome(m.name()); - return p(m.invoke(t.orSome(P.p(null))), name, params); - } catch(Exception e) { - throw new Error(e.getMessage(), e); - } + final F nameS = Name::value; + + return ms.filter(m -> { + //noinspection ObjectEquality + return m.isProperty() && + m.type() == Property.class && + !m.element().isAnnotationPresent(NoCheck.class) && + !declaringClass.isAnnotationPresent(NoCheck.class) && + (categories.length == 0 || array(categories).exists(p.f(m.element()))) && + (t.isSome() || isStatic(m.modifiers())); + }).map(m -> { + try { + final Option params = fromNull(m.element().getAnnotation(CheckParams.class)).orElse(fromNull(declaringClass.getAnnotation(CheckParams.class))); + final String name = fromNull(m.element().getAnnotation(Name.class)).map(nameS).orSome(m.name()); + return p(m.invoke(t.orSome(P.p(null))), name, params); + } catch(Exception e) { + throw new Error(e.getMessage(), e); } }); } diff --git a/quickcheck/src/main/java/fj/test/reflect/CheckParams.java b/quickcheck/src/main/java/fj/test/reflect/CheckParams.java index 725a7504..7951b40e 100644 --- a/quickcheck/src/main/java/fj/test/reflect/CheckParams.java +++ b/quickcheck/src/main/java/fj/test/reflect/CheckParams.java @@ -1,6 +1,6 @@ package fj.test.reflect; -import fj.P1; +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 a7e8be3c..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; @@ -36,14 +33,12 @@ public static void main(final String... args) { exit(441); } else { try { - check(forName(args[0]), array(args).toList().tail()).foreachDoEffect(new Effect1>() { - public void f(final P2 r) { - summary.print(r._2()); - out.println(" (" + r._1() + ')'); - } + check(forName(args[0]), array(args).toList().tail()).foreachDoEffect(r -> { + summary.print(r._2()); + out.println(" (" + r._1() + ')'); }); } catch(ClassNotFoundException e) { - System.err.println(e); + e.printStackTrace(); //noinspection CallToSystemExit exit(144); } diff --git a/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java index 7f2b04ce..8b90d57c 100644 --- a/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java +++ b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java @@ -29,14 +29,14 @@ public PropertyTestRunner(Class clas) { } @Override - public Description getDescription() { + public final Description getDescription() { Description suite = Description.createSuiteDescription(clas); filteredTests.foreachDoEffect(p -> suite.addChild(p._3())); return suite; } @Override - public void run(RunNotifier notifier) { + public final void run(RunNotifier notifier) { filteredTests.foreachDoEffect(p -> { Description desc = p._3(); notifier.fireTestStarted(desc); @@ -58,15 +58,14 @@ 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 - public void filter(Filter filter) throws NoTestsRemainException { + public final void filter(Filter filter) throws NoTestsRemainException { filteredTests = allTests.filter(p -> filter.shouldRun(p._3())); if (filteredTests.isEmpty()) { throw new NoTestsRemainException(); } } diff --git a/quickcheck/src/test/java/fj/data/test/TestCheck.java b/quickcheck/src/test/java/fj/data/test/TestCheck.java index 10c59a1c..3a4e6a80 100644 --- a/quickcheck/src/test/java/fj/data/test/TestCheck.java +++ b/quickcheck/src/test/java/fj/data/test/TestCheck.java @@ -1,19 +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.*; -import static fj.test.Arbitrary.arbLong; -import static fj.test.Coarbitrary.coarbInteger; -import static fj.test.Coarbitrary.coarbLong; import static fj.test.Property.prop; import static fj.test.Property.property; import static org.junit.Assert.*; @@ -22,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(arbitrary(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 6f7795aa..0d547149 100644 --- a/quickcheck/src/test/java/fj/data/test/TestNull.java +++ b/quickcheck/src/test/java/fj/data/test/TestNull.java @@ -1,32 +1,18 @@ 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.*; -import static fj.test.Arbitrary.arbLong; -import static fj.test.Coarbitrary.coarbInteger; -import static fj.test.Coarbitrary.coarbLong; import static fj.test.Property.prop; import static fj.test.Property.property; -/** - * Created by MarkPerry on 3/07/2014. - */ public class TestNull { @Test public void testShowNullParameters() { - Property p = property(arbitrary(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 new file mode 100644 index 00000000..c13480a1 --- /dev/null +++ b/quickcheck/src/test/java/fj/test/GenTest.java @@ -0,0 +1,213 @@ +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; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public final class GenTest { + + private static final List AS = list('A', 'B', 'C'); + + @Test + public void testCombinationOf_none() { + Gen> instance = combinationOf(0, AS); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); + } + + @Test + public void testCombinationOf_one() { + Gen> instance = combinationOf(1, AS); + testPick(100, instance, actual -> { + assertEquals(1, actual.length()); + assertTrue(AS.exists(a -> a.equals(actual.head()))); + }); + } + + @Test + public void testCombinationOf_two() { + Gen> instance = combinationOf(2, AS); + testPick(100, instance, actual -> { + assertEquals(2, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> charOrd.isGreaterThan(a, l.head())))); + }); + } + + @Test + public void testCombinationOf_three() { + Gen> instance = combinationOf(3, AS); + testPick(100, instance, actual -> { + assertEquals(3, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> charOrd.isGreaterThan(a, l.head())))); + }); + } + + @Test + public void testSelectionOf_none() { + Gen> instance = selectionOf(0, AS); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); + } + + @Test + public void testSelectionOf_one() { + Gen> instance = selectionOf(1, AS); + testPick(100, instance, actual -> { + assertEquals(1, actual.length()); + assertTrue(AS.exists(a -> a.equals(actual.head()))); + }); + } + + @Test + public void testSelectionOf_two() { + Gen> instance = selectionOf(2, AS); + testPick(100, instance, actual -> { + assertEquals(2, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> !charOrd.isLessThan(a, l.head())))); + }); + } + + @Test + public void testSelectionOf_three() { + Gen> instance = selectionOf(3, AS); + testPick(100, instance, actual -> { + assertEquals(3, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> !charOrd.isLessThan(a, l.head())))); + }); + } + + @Test + public void testSelectionOf_four() { + Gen> instance = selectionOf(4, AS); + testPick(100, instance, actual -> { + assertEquals(4, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> !charOrd.isLessThan(a, l.head())))); + }); + } + + @Test + public void testPermutationOf_none() { + Gen> instance = permutationOf(0, AS); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); + } + + @Test + public void testPermutationOf_one() { + Gen> instance = permutationOf(1, AS); + testPick(100, instance, actual -> { + assertEquals(1, actual.length()); + assertTrue(AS.exists(a -> a.equals(actual.head()))); + }); + } + + @Test + public void testPermutationOf_two() { + Gen> instance = combinationOf(2, AS); + testPick(100, instance, actual -> { + assertEquals(2, actual.length()); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> !a.equals(l.head())))); + }); + } + + @Test + public void testPermutationOf_three() { + Gen> instance = permutationOf(3, AS); + testPick(100, instance, actual -> { + assertEquals(3, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + assertTrue(actual.tails().forall(l -> l.isEmpty() || l.tail().forall(a -> !a.equals(l.head())))); + }); + } + + @Test + public void testWordOf_none() { + Gen> instance = wordOf(0, AS); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); + } + + @Test + public void testWordOf_one() { + Gen> instance = wordOf(1, AS); + testPick(100, instance, actual -> { + assertEquals(1, actual.length()); + assertTrue(AS.exists(a -> a.equals(actual.head()))); + }); + } + + @Test + public void testWordOf_two() { + Gen> instance = wordOf(2, AS); + testPick(100, instance, actual -> { + assertEquals(2, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + }); + } + + @Test + public void testWordOf_three() { + Gen> instance = wordOf(3, AS); + testPick(100, instance, actual -> { + assertEquals(3, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + }); + } + + @Test + public void testWordOf_four() { + Gen> instance = wordOf(4, AS); + testPick(100, instance, actual -> { + assertEquals(4, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + }); + } + + @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)))); + }); + } + + @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); + } + +} diff --git a/quickcheck/src/test/java/fj/test/TestBool.java b/quickcheck/src/test/java/fj/test/TestBool.java new file mode 100644 index 00000000..a2b9cae7 --- /dev/null +++ b/quickcheck/src/test/java/fj/test/TestBool.java @@ -0,0 +1,18 @@ +package fj.test; + +import fj.data.test.PropertyAssert; +import org.junit.Test; + +import static fj.test.Arbitrary.arbBoolean; +import static fj.test.Bool.bool; +import static fj.test.Property.property; + +public class TestBool { + + @Test + public void testBool() { + final Property p = property(arbBoolean, arbBoolean, (m1, m2) -> bool(m1.equals(m2)) + .implies(m1 == m2)); + PropertyAssert.assertResult(p); + } +} diff --git a/quickcheck/src/test/java/fj/test/TestRand.java b/quickcheck/src/test/java/fj/test/TestRand.java index 9df9639a..ea2cbce8 100644 --- a/quickcheck/src/test/java/fj/test/TestRand.java +++ b/quickcheck/src/test/java/fj/test/TestRand.java @@ -1,14 +1,14 @@ package fj.test; +import fj.Equal; import fj.Ord; +import fj.data.List; import fj.data.Stream; +import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 4/06/2015. - */ public class TestRand { @Test @@ -17,8 +17,22 @@ public void testRandLowHighInclusive() { int max = 10; int n = 100; Stream s = Stream.range(0, n).map(i -> Rand.standard.choose(min, max)).sort(Ord.intOrd); -// System.out.println(s); +// System.out.println(s.toList()); assertTrue(s.head() == min && s.last() == max); } + @Test + public void testReseed() { + Rand rand1 = Rand.standard.reseed(42); + List s1 = + List.range(0, 10).map(i -> rand1.choose(Integer.MIN_VALUE, Integer.MAX_VALUE)); + + Rand rand2 = rand1.reseed(42); + List s2 = + List.range(0, 10).map(i -> rand2.choose(Integer.MIN_VALUE, Integer.MAX_VALUE)); + + assertTrue(s1.zip(s2).forall(p -> p._1().equals(p._2()))); + Assert.assertFalse(s1.allEqual(Equal.intEqual)); + } + } 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"