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/.gitignore b/.gitignore index 28b13f3f..d3f6bf89 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,7 @@ build .classpath .project .DS_Store - +MANIFEST.MF +*/bin/** +*~ +/.nb-gradle/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..2fb30bf7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,31 @@ +# Use faster, Docker-based container (instead of OpenVZ) +sudo: false + +language: java + +jdk: + - openjdk8 + +matrix: + fast_finish: true + + include: + + allow_failures: + +script: + - ./gradlew build --no-daemon + +env: + global: + - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU= + - secure: QAxhjqLRa+WHKIzgIJPZ/rM5a5uzqG7E5rsC0YvB25cO712oYXmzsYPia/oSp0chXlYLYMfk2UnLeQCSx2e6ogXRRRa977Q+B33Nt0Hd9SGLtduv6DBrbA2ehLU12Ib4DWe5VhF5eueAunycYcllTvqA5h+pzTtEVbd68ZHncM4= + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..f24adb94 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +www.functionaljava.org \ No newline at end of file diff --git a/README.adoc b/README.adoc index 8e15dbf8..ecf403e2 100644 --- a/README.adoc +++ b/README.adoc @@ -1,16 +1,20 @@ = Functional Java +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. 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 5 to 8 and beyond. +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). -* Immutable Collections - array, list, vector, stream, set, map, finger tree, heterogenous list. -* Other Abstractions - monoid, semigroup, natural, random number generator, reader, writer, state, input/output, parser, zipper, specification based testing, actors, concurrency and type conversion. +* 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, 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 @@ -18,20 +22,25 @@ 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://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 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 two published artifacts: +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 version is `4.2-beta-1`. 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.2-beta-1" -compile "org.functionaljava:functionaljava-java8:4.2-beta-1" +compile "org.functionaljava:functionaljava:5.0" +compile "org.functionaljava:functionaljava-quickcheck:5.0" +compile "org.functionaljava:functionaljava-java-core:5.0" ---- and in Maven: @@ -39,24 +48,27 @@ and in Maven: org.functionaljava functionaljava - 4.2-beta-1 + 5.0 + + + org.functionaljava + functionaljava-quickcheck + 5.0 org.functionaljava - functionaljava-java8 - 4.2-beta-1 + functionaljava-java-core + 5.0 ---- == Building -FunctionalJava uses the Retro Lambda project to backport Java 8 lambdas to Java 7 bytecode. This requires access to both JDK 7 and 8. The build system requires the environment variables `JAVA7_HOME` and `JAVA8_HOME` to refer to the appropriate directories. - -Building is done using Gradle 2.0. In the root directory run: +Building is done using Java 8 and Gradle 7.4. In the root directory run: ---- ./gradlew ---- -This 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 @@ -64,7 +76,9 @@ A more complete description of the features mentioned above are: * Basic Data Structures ** Functions with arity 1 to 8 (`fj.F`). -** Functions with arity 0 to 8 that can produce exceptions (`fj.TryCatch`). +** Functions with arity 0 to 8 that can produce exceptions (`fj.Try`). +** Functions with arity 0 to 8 that have a void return (`fj.Effect`). +** Functions with arity 0 to 8 that have a void return and can throw an exception (`fj.TryEffect`). ** Products with arity 1 to 8 (`fj.P`). ** Unit type (`fj.Unit`). ** Optional value - _type-safe null_ (`fj.data.Option`). @@ -79,6 +93,8 @@ 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`). ** Semigroup (`fj.Semigroup`). @@ -93,7 +109,13 @@ A more complete description of the features mentioned above are: ** Zipper implementations for streams and trees. ** Automated specification-based testing framework (`fj.test`). ** Fully operational Actors for parallel computations (`fj.control.parallel`) and layered abstractions such as parallel-map, map-reduce, parallel-zip. +** Optics for updating immutable data including lens, prism, iso, optional, traversal, getter, fold and setter. Inspired by the Scala Monocle library (https://github.com/julien-truffaut/Monocle) and the Haskell lens library (https://github.com/ekmett/lens). +** Void, a logically uninhabited type. == License -link:etc/LICENCE[The Functional Java license] uses the BSD 3 license, available at (3-clause license, https://en.wikipedia.org/wiki/BSD_licenses[]). \ No newline at end of file +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 c71560d2..c89c9536 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,27 @@ defaultTasks 'build' -ext { +apply plugin: "com.github.ben-manes.versions" + +buildscript { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } + + dependencies { + 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 + } } + if (JavaVersion.current().isJava8Compatible()) { allprojects { tasks.withType(Javadoc) { @@ -14,15 +32,24 @@ if (JavaVersion.current().isJava8Compatible()) { allprojects { - ext { + apply plugin: "jacoco" + + jacoco { + toolVersion = "0.8.7" + } + + defaultTasks "build" + + ext { isSnapshot = true - fjBaseVersion = "4.2-beta-2" + fjBaseVersion = "5.1" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.1" + fjConsumeVersion = "5.0" signModule = false + uploadModule = false projectTitle = "Functional Java" projectName = "functionaljava" @@ -32,50 +59,102 @@ 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.11" + junitCompile = "junit:junit:4.13.2" + junitRuntime = "org.junit.vintage:junit-vintage-engine:5.8.2" + + displayCompilerWarnings = true + generateTestReports = false + } + + repositories { + mavenLocal() + mavenCentral() } version = fjVersion group = "org.functionaljava" + } subprojects { - defaultTasks "build" buildscript { repositories { + mavenLocal() mavenCentral() } - - dependencies { - classpath 'me.tatarka:gradle-retrolambda:1.3.1' - } } - repositories { - mavenCentral() + apply from: "$rootDir/lib.gradle" + apply plugin: "java-library" + apply plugin: "eclipse" + + repositories { + mavenLocal() + mavenCentral() maven { url sonatypeRepositoryUrl } - } - apply from: "$rootDir/lib.gradle" - apply plugin: "maven" - apply plugin: "java" - apply plugin: "signing" + tasks.withType(JavaCompile) { + if (displayCompilerWarnings) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } + } + tasks.withType(Test).configureEach { + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + if (!generateTestReports) { + reports.html.required = false + reports.junitXml.required = false + } + } + + +} + +task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { + 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.required = true + xml.required = true + } +} + +configure(subprojects.findAll { it.name != "props-core" }) { + + apply plugin: "maven-publish" + apply plugin: "signing" + apply plugin: "biz.aQute.bnd.builder" sourceCompatibility = "1.8" + javadoc { + } + task javadocJar(type: Jar, dependsOn: "javadoc") { classifier = 'javadoc' from "build/docs/javadoc" @@ -93,18 +172,33 @@ subprojects { } jar { - version project.fjVersion - manifest { - attributes 'Signature-Version': project.fjVersion - } + 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 { + project { + natures 'org.eclipse.pde.PluginNature' + buildCommand 'org.eclipse.pde.ManifestBuilder' + buildCommand 'org.eclipse.pde.SchemaBuilder' + } + } -task wrapper(type: Wrapper) { - gradleVersion = '2.0' + // Output MANIFEST.MF statically so eclipse can see it for plugin development + task eclipsePluginManifest(dependsOn: jar) doLast { + file("META-INF").mkdirs() + jar.manifest.writeTo(file("META-INF/MANIFEST.MF")) + } + + eclipseProject.dependsOn eclipsePluginManifest } -task env << { +task env doLast { println System.getenv() -} \ No newline at end of file +} 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 57c82333..45dadefb 100644 --- a/consume/build.gradle +++ b/consume/build.gradle @@ -1,4 +1,9 @@ +archivesBaseName = "${project.projectName}-${project.name}" + dependencies { - compile("$group:$projectName:$fjConsumeVersion") + api "$group:$projectName:$fjConsumeVersion" + + testImplementation junitCompile + testRuntimeOnly junitRuntime } diff --git a/consume/src/main/java/fj/consume/euler/Problem1.java b/consume/src/main/java/fj/consume/euler/Problem1.java index 9868d37c..d3bf7962 100644 --- a/consume/src/main/java/fj/consume/euler/Problem1.java +++ b/consume/src/main/java/fj/consume/euler/Problem1.java @@ -1,8 +1,5 @@ package fj.consume.euler; -import fj.F; -import fj.data.Stream; - import static fj.data.List.range; import static fj.function.Integers.sum; @@ -14,13 +11,11 @@ public class Problem1 { public static void main(final String[] args) { - java7(); + calc(); } - public static void java7() { - out.println(sum(range(0, 1000).filter(new F() { - public Boolean f(final Integer a) { return a % 3 == 0 || a % 5 == 0;} - }))); + public static void calc() { + out.println(sum(range(0, 1000).filter(a -> a % 3 == 0 || a % 5 == 0))); } } diff --git a/consume/src/test/java/fj/EmptyTest.java b/consume/src/test/java/fj/EmptyTest.java new file mode 100644 index 00000000..13675822 --- /dev/null +++ b/consume/src/test/java/fj/EmptyTest.java @@ -0,0 +1,15 @@ +package fj; + +import org.junit.Ignore; +import org.junit.Test; + +import org.junit.Assert; + +public class EmptyTest { + + @Ignore @Test + public void missing() { + Assert.fail("not implemented"); + + } +} diff --git a/core/build.gradle b/core/build.gradle index ae71be3f..282ec191 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,23 +1,18 @@ -apply plugin: 'retrolambda' - 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 - -retrolambda { - jdk System.getenv("JAVA8_HOME") - oldJdk System.getenv("JAVA7_HOME") - javaVersion JavaVersion.VERSION_1_7 -} +configureUpload(signingEnabled, signModule, uploadModule) diff --git a/core/src/main/java/fj/Bottom.java b/core/src/main/java/fj/Bottom.java index 6c475bd4..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() { @@ -38,11 +36,9 @@ public static Error error(final String s) { * @return A thunk that throws an error using the given message when evaluated. */ public static P1 error_(final String s) { - return new P1() { - @Override public A _1() { + return P.lazy(() -> { throw new Error(s); - } - }; + }); } /** @@ -52,10 +48,8 @@ public static P1 error_(final String s) { * @return A function that throws an error using the given message, ignoring its argument. */ public static F errorF(final String s) { - return new F() { - public B f(final A a) { + return a -> { throw new Error(s); - } }; } @@ -76,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); } @@ -87,11 +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 new F() { - public String f(final Throwable t) { - return t.toString(); - } - }; + return Throwable::toString; } /** @@ -100,10 +90,6 @@ public String f(final Throwable t) { * @return A function that returns the getMessage for a throwable. */ public static F eMessage() { - return new F() { - public String f(final Throwable t) { - return 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 9e02c4e4..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; @@ -29,31 +26,24 @@ private Class(final java.lang.Class c) { */ public List> inheritance() { return unfold( - new F, Option, java.lang.Class>>>() { - public Option, java.lang.Class>> f( - final java.lang.Class c) { - if (c == null) - return none(); - else { - final P2, java.lang.Class> p = - new P2, java.lang.Class>() { - public java.lang.Class _1() { - return c; - } + (java.lang.Class c2) -> { + if (c2 == null) + return none(); + else { + final P2, java.lang.Class> p = + new P2, java.lang.Class>() { + public java.lang.Class _1() { + return c2; + } - @SuppressWarnings({"unchecked"}) - public java.lang.Class _2() { - return c.getSuperclass(); - } - }; - return some(p); - } - } - }, c).map(new F, Class>() { - public Class f(final java.lang.Class c) { - return clas(c); - } - }); + @SuppressWarnings("unchecked") + public java.lang.Class _2() { + return c2.getSuperclass(); + } + }; + return some(p); + } + }, c).map(Class::clas); } /** @@ -109,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; } @@ -130,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 ed6e86ab..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,36 +174,20 @@ public static Option fromChar(final char c) { /** * First-class conversion from digit to a long. */ - public static final F toLong = new F() { - public Long f(final Digit d) { - return d.toLong(); - } - }; + public static final F toLong = Digit::toLong; /** * First-class conversion from a long to a digit. */ - public static final F fromLong = new F() { - public Digit f(final Long i) { - return fromLong(i); - } - }; + public static final F fromLong = Digit::fromLong; /** * First-class conversion from a digit to a character. */ - public static final F toChar = new F() { - public Character f(final Digit d) { - return d.toChar(); - } - }; + public static final F toChar = Digit::toChar; /** * First-class conversion from a character to a digit. */ - public static final F> fromChar = new F>() { - public Option f(final Character c) { - return 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 c9bb252d..2b5dcd6f 100644 --- a/core/src/main/java/fj/Effect.java +++ b/core/src/main/java/fj/Effect.java @@ -14,15 +14,16 @@ /** * Represents a side-effect. - * - * @version %build.number% */ -public class Effect { +public final class Effect { private Effect() {} public static P1 f(Effect0 e) { - return P.lazy(u -> unit()); + return P.lazy(() -> { + e.f(); + return unit(); + }); } /** @@ -30,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(); @@ -92,20 +93,13 @@ public static F5 f(Effect5 z * @param f The function to map over the effect. * @return An effect after a contra-variant map. */ - public final Effect1 comap(Effect1 e1, final F f) { - return new Effect1() { - public void f(final B b) { - e1.f(f.f(b)); - } - }; + public static Effect1 contramap(Effect1 e1, final F f) { + return b -> e1.f(f.f(b)); } public static Effect1 lazy(final F f) { - return new Effect1() { - public void f(final A a) { - f.f(a); - } - }; + return f::f; + } // public static void f(Effect1 ) diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index e510671f..cba4a589 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -1,17 +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.Array; -import fj.data.Either; -import fj.data.LazyString; -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.Validation; +import fj.data.optic.Traversal; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; @@ -19,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; } /** @@ -43,7 +98,18 @@ 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); + } + + /** + * Returns true if the two given arguments are not equal, false otherwise. + * + * @param a1 An object to test for inequality against another. + * @param a2 An object to test for inequality against another. + * @return true if the two given arguments are not equal, false otherwise. + */ + public boolean notEq(final A a1, final A a2) { + return !def.equal(a1, a2); } /** @@ -52,11 +118,7 @@ public boolean eq(final A a1, final A a2) { * @return A function that returns true if the two given arguments are equal. */ public F2 eq() { - return new F2() { - public Boolean f(final A a, final A a1) { - return eq(a, a1); - } - }; + return def::equal; } /** @@ -66,11 +128,7 @@ public Boolean f(final A a, final A a1) { * @return A function that returns true if the given argument equals the argument to this method. */ public F eq(final A a) { - return new F() { - public Boolean f(final A a1) { - return eq(a, a1); - } - }; + return def.equal(a); } /** @@ -79,18 +137,92 @@ public Boolean f(final A a1) { * @param f The function to map. * @return A new equal. */ - public Equal comap(final F f) { - return equal(F1Functions.o(F1Functions.o(F1Functions.andThen(f), this.f), f)); + public Equal contramap(final 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); } /** @@ -101,13 +233,15 @@ public static Equal equal(final F> f) { * equality. */ public static Equal anyEqual() { - return new Equal(new F>() { - public F f(final A a1) { - return new F() { - public Boolean f(final A a2) { - return 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); } }); } @@ -162,6 +296,11 @@ public Boolean f(final A a2) { */ 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. */ @@ -171,42 +310,35 @@ public Boolean f(final A a2) { * An equal instance for the {@link StringBuffer} type. */ public static final Equal stringBufferEqual = - new Equal(new F>() { - public F f(final StringBuffer sb1) { - return new F() { - public Boolean f(final StringBuffer sb2) { - if (sb1.length() == sb2.length()) { - for (int i = 0; i < sb1.length(); i++) - if (sb1.charAt(i) != sb2.charAt(i)) - return false; - return true; - } else - return false; - } - }; - } + equalDef((sb1, sb2) -> { + if (sb1.length() == sb2.length()) { + for (int i = 0; i < sb1.length(); i++) + if (sb1.charAt(i) != sb2.charAt(i)) + return false; + return true; + } else + return false; }); /** * An equal instance for the {@link StringBuilder} type. */ public static final Equal stringBuilderEqual = - new Equal(new F>() { - public F f(final StringBuilder sb1) { - return new F() { - public Boolean f(final StringBuilder sb2) { - if (sb1.length() == sb2.length()) { - for (int i = 0; i < sb1.length(); i++) - if (sb1.charAt(i) != sb2.charAt(i)) - return false; - return true; - } else - return false; - } - }; - } + equalDef((sb1, sb2) -> { + if (sb1.length() == sb2.length()) { + for (int i = 0; i < sb1.length(); i++) + if (sb1.charAt(i) != sb2.charAt(i)) + return false; + return true; + } else + 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. * @@ -215,18 +347,29 @@ public Boolean f(final StringBuilder sb2) { * @return An equal instance for the {@link Either} type. */ public static Equal> eitherEqual(final Equal ea, final Equal eb) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final Either e1) { - return new F, Boolean>() { - public Boolean f(final Either e2) { - return 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. * @@ -235,7 +378,7 @@ public Boolean f(final Either e2) { * @return An equal instance for the {@link Validation} type. */ public static Equal> validationEqual(final Equal ea, final Equal eb) { - return eitherEqual(ea, eb).comap(Validation.either()); + return eitherEqual(ea, eb).contramap(Validation.either()); } /** @@ -245,25 +388,20 @@ public static Equal> validationEqual(final Equal ea, * @return An equal instance for the {@link List} type. */ public static Equal> listEqual(final Equal ea) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final List a1) { - return new F, Boolean>() { - public Boolean f(final List a2) { - List x1 = a1; - List x2 = a2; - - while (x1.isNotEmpty() && x2.isNotEmpty()) { - if (!ea.eq(x1.head(), x2.head())) - return false; - - x1 = x1.tail(); - x2 = x2.tail(); - } - - return x1.isEmpty() && x2.isEmpty(); - } - }; + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { + List x1 = a1; + List x2 = a2; + + while (x1.isNotEmpty() && x2.isNotEmpty()) { + if (!eaDef.equal(x1.head(), x2.head())) + return false; + + x1 = x1.tail(); + x2 = x2.tail(); } + + return x1.isEmpty() && x2.isEmpty(); }); } @@ -274,7 +412,7 @@ public Boolean f(final List a2) { * @return An equal instance for the {@link NonEmptyList} type. */ public static Equal> nonEmptyListEqual(final Equal ea) { - return listEqual(ea).comap(NonEmptyList.toList_()); + return listEqual(ea).contramap(NonEmptyList.toList_()); } /** @@ -284,16 +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 new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final Option o1) { - return new F, Boolean>() { - public Boolean f(final Option o2) { - return 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 streamEqual(e).contramap(Seq::toStream); } /** @@ -303,50 +440,74 @@ public Boolean f(final Option o2) { * @return An equal instance for the {@link Stream} type. */ public static Equal> streamEqual(final Equal ea) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final Stream a1) { - return new F, Boolean>() { - public Boolean f(final Stream a2) { - Stream x1 = a1; - Stream x2 = a2; - - while (x1.isNotEmpty() && x2.isNotEmpty()) { - if (!ea.eq(x1.head(), x2.head())) - return false; - - x1 = x1.tail()._1(); - x2 = x2.tail()._1(); - } - - return x1.isEmpty() && x2.isEmpty(); - } - }; + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { + Stream x1 = a1; + Stream x2 = a2; + + while (x1.isNotEmpty() && x2.isNotEmpty()) { + if (!eaDef.equal(x1.head(), x2.head())) + return false; + + x1 = x1.tail()._1(); + x2 = x2.tail()._1(); } + + return x1.isEmpty() && x2.isEmpty(); }); } /** + * 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 new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final Array a1) { - return new F, Boolean>() { - public Boolean f(final Array a2) { - if (a1.length() == a2.length()) { - for (int i = 0; i < a1.length(); i++) { - if (!ea.eq(a1.get(i), a2.get(i))) - return false; - } - return true; - } else - return false; - } - }; - } + Definition eaDef = ea.def; + return equalDef((a1, a2) -> { + if (a1.length() == a2.length()) { + for (int i = 0; i < a1.length(); i++) { + if (!eaDef.equal(a1.get(i), a2.get(i))) + return false; + } + return true; + } else + return false; }); } @@ -357,11 +518,18 @@ public Boolean f(final Array a2) { * @return An equal instance for the {@link Tree} type. */ public static Equal> treeEqual(final Equal ea) { - return new Equal>(curry(new F2, Tree, Boolean>() { - public Boolean f(final Tree t1, final Tree t2) { - return ea.eq(t1.root(), t2.root()) && p1Equal(streamEqual(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()); + } - })); + }); } /** @@ -371,15 +539,7 @@ public Boolean f(final Tree t1, final Tree t2) { * @return An equal instance for a product-1. */ public static Equal> p1Equal(final Equal ea) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P1 p1) { - return new F, Boolean>() { - public Boolean f(final P1 p2) { - return ea.eq(p1._1(), p2._1()); - } - }; - } - }); + return ea.contramap(P1.__1()); } /** @@ -390,15 +550,9 @@ public Boolean f(final P1 p2) { * @return An equal instance for a product-2. */ public static Equal> p2Equal(final Equal ea, final Equal eb) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P2 p1) { - return new F, Boolean>() { - public Boolean f(final P2 p2) { - return 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())); } /** @@ -410,15 +564,10 @@ public Boolean f(final P2 p2) { * @return An equal instance for a product-3. */ public static Equal> p3Equal(final Equal ea, final Equal eb, final Equal ec) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P3 p1) { - return new F, Boolean>() { - public Boolean f(final P3 p2) { - return 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())); } /** @@ -432,16 +581,12 @@ public Boolean f(final P3 p2) { */ public static Equal> p4Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P4 p1) { - return new F, Boolean>() { - public Boolean f(final P4 p2) { - return 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())); } /** @@ -457,16 +602,13 @@ public Boolean f(final P4 p2) { public static Equal> p5Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P5 p1) { - return new F, Boolean>() { - public Boolean f(final P5 p2) { - return 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())); } /** @@ -483,16 +625,14 @@ public Boolean f(final P5 p2) { public static Equal> p6Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee, final Equal ef) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P6 p1) { - return new F, Boolean>() { - public Boolean f(final P6 p2) { - return 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())); } /** @@ -511,17 +651,16 @@ public Boolean f(final P6 p2) { final Equal ec, final Equal ed, final Equal ee, final Equal ef, final Equal eg) { - return new Equal>(new F, F, Boolean>>() { - public F, Boolean> f(final P7 p1) { - return new F, Boolean>() { - public Boolean f(final P7 p2) { - return 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())); } /** @@ -545,18 +684,18 @@ public Boolean f(final P7 p2) { final Equal ef, final Equal eg, final Equal eh) { - return new Equal>( - new F, F, Boolean>>() { - public F, Boolean> f(final P8 p1) { - return new F, Boolean>() { - public Boolean f(final P8 p2) { - return 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())); } /** @@ -566,7 +705,7 @@ public Boolean f(final P8 p2) { * @return An equal instance for a vector-2. */ public static Equal> v2Equal(final Equal ea) { - return streamEqual(ea).comap(V2.toStream_()); + return streamEqual(ea).contramap(V2.toStream_()); } /** @@ -576,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).comap(V3.toStream_()); + return streamEqual(ea).contramap(V3.toStream_()); } /** @@ -586,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).comap(V4.toStream_()); + return streamEqual(ea).contramap(V4.toStream_()); } /** @@ -596,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).comap(V5.toStream_()); + return streamEqual(ea).contramap(V5.toStream_()); } /** @@ -606,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).comap(V6.toStream_()); + return streamEqual(ea).contramap(V6.toStream_()); } /** @@ -616,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).comap(V7.toStream_()); + return streamEqual(ea).contramap(V7.toStream_()); } /** @@ -626,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).comap(V8.toStream_()); + return streamEqual(ea).contramap(V8.toStream_()); } /** * An equal instance for lazy strings. */ - public static final Equal eq = streamEqual(charEqual).comap(LazyString.toStream); + public static final Equal eq = streamEqual(charEqual).contramap(LazyString::toStream); /** * An equal instance for the empty heterogeneous list. @@ -647,11 +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(new F2, HList.HCons, Boolean>() { - public Boolean f(final HList.HCons c1, final HList.HCons c2) { - return 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())); } /** @@ -661,10 +798,43 @@ public Boolean f(final HList.HCons c1, final HList.HCons c2) { * @return An equal instance for sets. */ public static Equal> setEqual(final Equal e) { - return equal(curry(new F2, Set, Boolean>() { - public Boolean f(final Set a, final Set b) { - return streamEqual(e).eq(a.toStream(), b.toStream()); - } - })); + return streamEqual(e).contramap(Set::toStream); } + + public static Equal> treeMapEqual(Equal k, Equal v) { + return streamEqual(p2Equal(k, v)).contramap(TreeMap::toStream); + } + + public static Equal> writerEqual(Equal eq1, Equal eq2) { + return p2Equal(eq1, eq2).contramap(Writer::run); + } + + /** + * Helper method to implement {@link Object#equals(Object)} correctly. DO NOT USE it for any other purpose. + * + * @param clazz the class in which the {@link Object#equals(Object)} is implemented + * @param self a reference to 'this' + * @param other the other object of the comparison + * @param equal an equal instance for the type of self (that use {@link #anyEqual()} if generic type). + * @return true if self and other are equal + */ + @SuppressWarnings("unchecked") + public static boolean equals0(final java.lang.Class clazz, final A self, final Object other, final Equal equal) { + return self == other || clazz.isInstance(other) && equal.eq(self, (A) other); + } + + /** + * Helper method to implement {@link Object#equals(Object)} correctly. DO NOT USE it for any other purpose. + * + * @param clazz the class in which the {@link Object#equals(Object)} is implemented + * @param self a reference to 'this' + * @param other the other object of the comparison + * @param equal a lazy equal instance for the type (that use {@link #anyEqual()} if generic type).. + * @return true if self and other are equal + */ + @SuppressWarnings("unchecked") + public static boolean equals0(final java.lang.Class clazz, final A self, final Object other, final F0> equal) { + return self == other || clazz.isInstance(other) && equal.f().eq(self, (A) other); + } + } 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 new file mode 100644 index 00000000..88d60a9e --- /dev/null +++ b/core/src/main/java/fj/F0.java @@ -0,0 +1,34 @@ +package fj; + +import fj.function.Effect0; +import fj.function.Try0; +import fj.function.TryEffect0; + +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 b0e84f00..00000000 --- a/core/src/main/java/fj/F1Functions.java +++ /dev/null @@ -1,846 +0,0 @@ -package fj; - -import fj.control.parallel.Actor; -import fj.control.parallel.Promise; -import fj.control.parallel.Strategy; -import fj.data.*; -import fj.function.Try1; - -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 new F() { - public B f(final C c) { - return 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 new F, F>() { - public F f(final F g) { - return 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 new F, F>() { - public F f(final F g) { - return 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 new F() { - @SuppressWarnings({"unchecked"}) - public C f(final A a) { - return 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 new F>, F>() { - public F f(final F> g) { - return 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 new F() { - @SuppressWarnings({"unchecked"}) - public C f(final A a) { - return 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 new F>, F>() { - public F f(final F> g) { - return 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 new F>() { - public F f(final A a1) { - return new F() { - @SuppressWarnings({"unchecked"}) - public C f(final A a2) { - return 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 new F>, F>>() { - public F> f(final F> g) { - return 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 new F>() { - public P1 f(final A a) { - return new P1() { - public B _1() { - return 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(u -> 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 new F, P1>() { - public P1 f(final P1 p) { - return 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 new F>() { - public Option f(final A a) { - return 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 new F, Option>() { - public Option f(final Option o) { - return 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 new F>() { - public List f(final A a) { - return 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 new F, List>() { - public List f(final List x) { - return 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 new F>() { - public Stream f(final A a) { - return 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 new F, Stream>() { - public Stream f(final Stream x) { - return 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 new F>() { - public Array f(final A a) { - return 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 new F, Array>() { - public Array f(final Array x) { - return x.map(f); - } - }; - } - - /** - * Returns a function that comaps over a given actor. - * - * @return A function that comaps over a given actor. - */ - static public F, Actor> comapActor(final F f) { - return new F, Actor>() { - public Actor f(final Actor a) { - return a.comap(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 new F, Promise>() { - public Promise f(final Promise p) { - return 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 new F, B>() { - public B f(final Either either) { - return either.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 new F, B>() { - public B f(final Either either) { - return either.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 new F, NonEmptyList>() { - public NonEmptyList f(final NonEmptyList list) { - return 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 new F>() { - public Set f(final A a) { - return 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 new F, Set>() { - public Set f(final Set set) { - return set.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 new F>() { - public Tree f(final A a) { - return 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 new F, TreeZipper>() { - public TreeZipper f(final TreeZipper zipper) { - return zipper.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 new F>() { - public Validation f(final A a) { - return 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 new F>() { - public Validation f(final A a) { - return 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 new F, Validation>() { - public Validation f(final Validation validation) { - return validation.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 new F, Validation>() { - public Validation f(final Validation validation) { - return validation.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 new F, B>() { - public B f(final Validation v) { - return 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 new F, B>() { - public B f(final Validation v) { - return 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), new F, Zipper>() { - public Zipper f(final Stream stream) { - return fromStream(stream).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 new F, Zipper>() { - public Zipper f(final Zipper zipper) { - return zipper.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> comapEqual(final F f) { - return new F, Equal>() { - public Equal f(final Equal equal) { - return equal.comap(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> comapHash(final F f) { - return new F, Hash>() { - public Hash f(final Hash hash) { - return hash.comap(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> comapShow(final F f) { - return new F, Show>() { - public Show f(final Show s) { - return s.comap(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 new F, P2>() { - public P2 f(final P2 aap2) { - return P2.map(f, aap2); - } - }; - } - - /** - * 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/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 fda0421b..00000000 --- a/core/src/main/java/fj/F2Functions.java +++ /dev/null @@ -1,366 +0,0 @@ -package fj; - -import fj.control.parallel.Promise; -import fj.data.*; -import fj.function.Try2; - -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 new F() { - public C f(final B b) { - return 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 new F>() { - public F f(final A a) { - return new F() { - public C f(final B b) { - return 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 new F2() { - public C f(final B b, final A a) { - return 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 new F, C>() { - public C f(final P2 p) { - return 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 new F2, Array, Array>() { - public Array f(final Array a, final Array b) { - return 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 new F2, Promise, Promise>() { - public Promise f(final Promise a, final Promise b) { - return 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 new F2, Iterable, IterableW>() { - public IterableW f(final Iterable a, final Iterable b) { - return 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 new F2, List, List>() { - public List f(final List a, final List b) { - return 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 new F2, NonEmptyList, NonEmptyList>() { - public NonEmptyList f(final NonEmptyList as, final NonEmptyList bs) { - return 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 new F2, Option, Option>() { - public Option f(final Option a, final Option b) { - return 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 new F2, Set, Set>() { - public Set f(final Set as, final Set 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 new F2, Stream, Stream>() { - public Stream f(final Stream as, final Stream bs) { - return 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()), new P1>>() { - public Stream> _1() { - return 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 new F2, Array, Array>() { - public Array f(final Array as, final Array bs) { - return 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 new F2, Iterable, Iterable>() { - public Iterable f(final Iterable as, final Iterable bs) { - return 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 new F2, List, List>() { - public List f(final List as, final List bs) { - return 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 new F2, Stream, Stream>() { - public Stream f(final Stream as, final Stream bs) { - return 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 new F2, NonEmptyList, NonEmptyList>() { - public NonEmptyList f(final NonEmptyList as, final NonEmptyList bs) { - return 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 new F2, Set, Set>() { - public Set f(final Set as, final Set bs) { - return 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()), new P1>>() { - public Stream> _1() { - return 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 new F2, Zipper, Zipper>() { - @SuppressWarnings({"unchecked"}) - public Zipper f(final Zipper ta, final Zipper 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 new F2, TreeZipper, TreeZipper>() { - @SuppressWarnings({"unchecked"}) - public TreeZipper f(final TreeZipper ta, final TreeZipper tb) { - final F2>, Stream>, Stream>> sf = zipStreamM(treeM(f)); - final - F2>, A, Stream>>>, - Stream>, B, Stream>>>, - Stream>, C, Stream>>>> - pf = - zipStreamM(new F2>, A, Stream>>, - P3>, B, Stream>>, - P3>, C, Stream>>>() { - public P3>, C, Stream>> f(final P3>, A, Stream>> pa, - final P3>, B, Stream>> pb) { - return 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/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/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/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/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/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/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/Function.java b/core/src/main/java/fj/Function.java index 96f62f85..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() { @@ -19,11 +17,7 @@ private Function() { * @return A function that is partially-applied to the given value. */ public static F, B> apply(final A a) { - return new F, B>() { - public B f(final F k) { - return k.f(a); - } - }; + return f -> f.f(a); } /** @@ -32,15 +26,7 @@ public B f(final F k) { * @return A function that composes two functions to produce a new function. */ public static F, F, F>> compose() { - return new F, F, F>>() { - public F, F> f(final F f) { - return new F, F>() { - public F f(final F g) { - return compose(f, g); - } - }; - } - }; + return f -> g -> compose(f, g); } /** @@ -51,11 +37,7 @@ public F f(final F g) { * @return A function that is the composition of the given arguments. */ public static F compose(final F f, final F g) { - return new F() { - public C f(final A a) { - return f.f(g.f(a)); - } - }; + return a -> f.f(g.f(a)); } /** @@ -66,15 +48,7 @@ public C f(final A a) { * @return A function that is the composition of the given arguments. */ public static F> compose2(final F f, final F> g) { - return new F>() { - public F f(final A a) { - return new F() { - public D f(final B b) { - return f.f(g.f(a).f(b)); - } - }; - } - }; + return a -> b -> f.f(g.f(a).f(b)); } @@ -84,15 +58,7 @@ public D f(final B b) { * @return A function that composes two functions to produce a new function. */ public static F, F, F>> andThen() { - return new F, F, F>>() { - public F, F> f(final F g) { - return new F, F>() { - public F f(final F f) { - return Function.andThen(g, f); - } - }; - } - }; + return g -> f -> andThen(g, f); } /** @@ -103,11 +69,7 @@ public F f(final F f) { * @return A function that is the composition of the given arguments. */ public static F andThen(final F g, final F f) { - return new F() { - public C f(final A a) { - return f.f(g.f(a)); - } - }; + return a -> f.f(g.f(a)); } /** @@ -116,11 +78,7 @@ public C f(final A a) { * @return The identity transformation. */ public static F identity() { - return new F() { - public A f(final A a) { - return a; - } - }; + return a -> a; } /** @@ -129,11 +87,7 @@ public A f(final A a) { * @return A function that given an argument, returns a function that ignores its argument. */ public static F> constant() { - return new F>() { - public F f(final B b) { - return constant(b); - } - }; + return Function::constant; } /** @@ -143,11 +97,7 @@ public F f(final B b) { * @return A function that ignores its argument to constantly produce the given value. */ public static F constant(final B b) { - return new F() { - public B f(final A a) { - return b; - } - }; + return a -> b; } /** @@ -157,11 +107,7 @@ public B f(final A a) { * @return A co- and contravariant function that invokes f on its argument. */ public static F vary(final F f) { - return new F() { - public B f(final A a) { - return f.f(a); - } - }; + return f::f; } /** @@ -170,11 +116,7 @@ public B f(final A a) { * @return A function that varies and covaries a function. */ public static F, F> vary() { - return new F, F>() { - public F f(final F f) { - return Function.vary(f); - } - }; + return Function::vary; } /** @@ -183,11 +125,7 @@ public F f(final F f) { * @return A function that takes a function and flips its arguments. */ public static F>, F>> flip() { - return new F>, F>>() { - public F> f(final F> f) { - return flip(f); - } - }; + return Function::flip; } /** @@ -197,15 +135,7 @@ public F> f(final F> f) { * @return The given function flipped. */ public static F> flip(final F> f) { - return new F>() { - public F f(final B b) { - return new F() { - public C f(final A a) { - return f.f(a).f(b); - } - }; - } - }; + return b -> a -> f.f(a).f(b); } /** @@ -215,11 +145,7 @@ public C f(final A a) { * @return The given function flipped. */ public static F2 flip(final F2 f) { - return new F2() { - public C f(final B b, final A a) { - return f.f(a, b); - } - }; + return (b, a) -> f.f(a, b); } /** @@ -228,11 +154,7 @@ public C f(final B b, final A a) { * @return A function that flips the arguments of a given function. */ public static F, F2> flip2() { - return new F, F2>() { - public F2 f(final F2 f) { - return flip(f); - } - }; + return Function::flip; } /** @@ -244,11 +166,7 @@ public F2 f(final F2 f) { * not apply the value, instead returning an empty optional value. */ public static F> nullable(final F f) { - return new F>() { - public Option f(final A a) { - return a == null ? Option.none() : Option.some(f.f(a)); - } - }; + return a -> a == null ? Option.none() : Option.some(f.f(a)); } /** @@ -258,15 +176,7 @@ public Option f(final A a) { * @return A curried form of the given function. */ public static F> curry(final F2 f) { - return new F>() { - public F f(final A a) { - return new F() { - public C f(final B b) { - return f.f(a, b); - } - }; - } - }; + return a -> b -> f.f(a, b); } /** @@ -286,11 +196,7 @@ public static F curry(final F2 f, final A a) { * @return An uncurried function. */ public static F>, F2> uncurryF2() { - return new F>, F2>() { - public F2 f(final F> f) { - return uncurryF2(f); - } - }; + return Function::uncurryF2; } /** @@ -300,11 +206,7 @@ public F2 f(final F> f) { * @return An uncurried function. */ public static F2 uncurryF2(final F> f) { - return new F2() { - public C f(final A a, final B b) { - return f.f(a).f(b); - } - }; + return (a, b) -> f.f(a).f(b); } /** @@ -314,19 +216,7 @@ public C f(final A a, final B b) { * @return A curried form of the given function. */ public static F>> curry(final F3 f) { - return new F>>() { - public F> f(final A a) { - return new F>() { - public F f(final B b) { - return new F() { - public D f(final C c) { - return f.f(a, b, c); - } - }; - } - }; - } - }; + return a -> b -> c -> f.f(a, b, c); } /** @@ -358,11 +248,7 @@ public static F curry(final F3 f, final A a, fina * @return An uncurried function. */ public static F>>, F3> uncurryF3() { - return new F>>, F3>() { - public F3 f(final F>> f) { - return uncurryF3(f); - } - }; + return Function::uncurryF3; } /** @@ -372,11 +258,7 @@ public F3 f(final F>> f) { * @return An uncurried function. */ public static F3 uncurryF3(final F>> f) { - return new F3() { - public D f(final A a, final B b, final C c) { - return f.f(a).f(b).f(c); - } - }; + return (a, b, c) -> f.f(a).f(b).f(c); } /** @@ -386,23 +268,7 @@ public D f(final A a, final B b, final C c) { * @return A curried form of the given function. */ public static F>>> curry(final F4 f) { - return 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 E f(final D d) { - return f.f(a, b, c, d); - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> f.f(a, b, c, d); } /** @@ -447,11 +313,7 @@ public static F curry(final F4 f, final A a * @return An uncurried function. */ public static F>>>, F4> uncurryF4() { - return new F>>>, F4>() { - public F4 f(final F>>> f) { - return uncurryF4(f); - } - }; + return Function::uncurryF4; } /** @@ -461,11 +323,7 @@ public F4 f(final F>>> f) { * @return An uncurried function. */ public static F4 uncurryF4(final F>>> f) { - return new F4() { - public E f(final A a, final B b, final C c, final D d) { - return f.f(a).f(b).f(c).f(d); - } - }; + return (a, b, c, d) -> f.f(a).f(b).f(c).f(d); } /** @@ -475,27 +333,7 @@ public E f(final A a, final B b, final C c, final D d) { * @return A curried form of the given function. */ public static F>>>> curry(final F5 f) { - return 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 f.f(a, b, c, d, e); - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f.f(a, b, c, d, e); } /** @@ -556,11 +394,7 @@ public E f(final A a, final B b, final C c, final D d) { * @return An uncurried function. */ public static F>>>>, F5> uncurryF5() { - return new F>>>>, F5>() { - public F5 f(final F>>>> f) { - return uncurryF5(f); - } - }; + return Function::uncurryF5; } /** @@ -570,11 +404,7 @@ public E f(final A a, final B b, final C c, final D d) { * @return An uncurried function. */ public static F5 uncurryF5(final F>>>> f) { - return new F5() { - public F$ f(final A a, final B b, final C c, final D d, final E e) { - return f.f(a).f(b).f(c).f(d).f(e); - } - }; + return (a, b, c, d, e) -> f.f(a).f(b).f(c).f(d).f(e); } /** @@ -584,31 +414,7 @@ public E f(final A a, final B b, final C c, final D d) { * @return A curried form of the given function. */ public static F>>>>> curry(final F6 f) { - return 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 G f(final F$ f$) { - return f.f(a, b, c, d, e, f$); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f$ -> f.f(a, b, c, d, e, f$); } /** @@ -617,11 +423,7 @@ public G f(final F$ f$) { * @return An uncurried function. */ public static F>>>>>, F6> uncurryF6() { - return new F>>>>>, F6>() { - public F6 f(final F>>>>> f) { - return uncurryF6(f); - } - }; + return Function::uncurryF6; } /** @@ -632,11 +434,7 @@ public G f(final F$ f$) { */ public static F6 uncurryF6( final F>>>>> f) { - return new F6() { - public G f(final A a, final B b, final C c, final D d, final E e, final F$ f$) { - return f.f(a).f(b).f(c).f(d).f(e).f(f$); - } - }; + return (a, b, c, d, e, f$) -> f.f(a).f(b).f(c).f(d).f(e).f(f$); } /** @@ -647,35 +445,7 @@ public G f(final A a, final B b, final C c, final D d, final E e, final F$ f$) { */ public static F>>>>>> curry( final F7 f) { - return 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 H f(final G g) { - return f.f(a, b, c, d, e, f$, g); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f$ -> g -> f.f(a, b, c, d, e, f$, g); } /** @@ -771,11 +541,7 @@ public H f(final G g) { * @return An uncurried function. */ public static F>>>>>>, F7> uncurryF7() { - return new F>>>>>>, F7>() { - public F7 f(final F>>>>>> f) { - return uncurryF7(f); - } - }; + return Function::uncurryF7; } /** @@ -786,11 +552,7 @@ public H f(final G g) { */ public static F7 uncurryF7( final F>>>>>> f) { - return new F7() { - public H f(final A a, final B b, final C c, final D d, final E e, final F$ f$, final G g) { - return f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g); - } - }; + return (a, b, c, d, e, f$, g) -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g); } /** @@ -801,39 +563,7 @@ public H f(final A a, final B b, final C c, final D d, final E e, final F$ f$, f */ public static F>>>>>>> curry( final F8 f) { - return 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 I f(final H h) { - return f.f(a, b, c, d, e, f$, g, h); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f$ -> g -> h -> f.f(a, b, c, d, e, f$, g, h); } /** @@ -950,11 +680,7 @@ public I f(final H h) { * @return An uncurried function. */ public static F>>>>>>>, F8> uncurryF8() { - return new F>>>>>>>, F8>() { - public F8 f(final F>>>>>>> f) { - return uncurryF8(f); - } - }; + return Function::uncurryF8; } /** @@ -965,11 +691,7 @@ public I f(final H h) { */ public static F8 uncurryF8( final F>>>>>>> f) { - return new F8() { - public I f(final A a, final B b, final C c, final D d, final E e, final F$ f$, final G g, final H h) { - return f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g).f(h); - } - }; + return (a, b, c, d, e, f$, g, h) -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g).f(h); } /** @@ -983,11 +705,7 @@ public I f(final A a, final B b, final C c, final D d, final E e, final F$ f$, f * that is applied to the argument again. */ public static F bind(final F ma, final F> f) { - return new F() { - public B f(final C m) { - return f.f(ma.f(m)).f(m); - } - }; + return m -> f.f(ma.f(m)).f(m); } /** @@ -998,15 +716,18 @@ public B f(final C m) { * @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, new F, F>() { - public F f(final F f) { - return compose(new F() { - public B f(final A a) { - return f.f(a); - } - }, 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)); } /** @@ -1039,11 +760,7 @@ public static F> on(final F> a, final F f) * @return A function of arity-2 promoted to compose with two functions. */ public static F, F, F>> lift(final F> f) { - return curry(new F2, F, F>() { - public F f(final F ca, final F cb) { - return bind(ca, cb, f); - } - }); + return curry((ca, cb) -> bind(ca, cb, f)); } /** @@ -1053,207 +770,95 @@ public F f(final F ca, final F cb) { * @return A function of arity-1 whose argument is substituted for both parameters of f. */ 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 new F() { - public C f(final A a) { - return uncurryF2(f).f(a, b); - } - }; + return a -> uncurryF2(f).f(a, 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 new F>() { - public F f(final A a) { - return new F() { - public D f(final B b) { - return uncurryF3(f).f(a, b, c); - } - }; - } - }; + return a -> b -> uncurryF3(f).f(a, b, c); } /** * 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 new F>>() { - public F> f(final A a) { - return new F>() { - public F f(final B b) { - return new F() { - public E f(final C c) { - return uncurryF4(f).f(a, b, c, d); - } - }; - } - }; - } - }; + return a -> b -> c -> uncurryF4(f).f(a, b, c, d); } /** * Partial application of the fifth argument to the supplied function to get a function of type - * A -> 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) { - return 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 uncurryF5(f).f(a, b, c, d, e); - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> uncurryF5(f).f(a, b, c, d, e); } /** * Partial application of the sixth argument to the supplied function to get a function of type - * A -> 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$) { - return 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 G f(final E e) { - return uncurryF6(f).f(a, b, c, d, e, f$); - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> uncurryF6(f).f(a, b, c, d, e, f$); } /** * Partial application of the seventh argument to the supplied function to get a function of type - * A -> 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) { - return 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 H f(final F$ f$) { - return uncurryF7(f).f(a, b, c, d, e, f$, g); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f$ -> uncurryF7(f).f(a, b, c, d, e, f$, g); } /** * Partial application of the eigth argument to the supplied function to get a function of type - * A -> 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) { - return 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 I f(final G g) { - return uncurryF8(f).f(a, b, c, d, e, f$, g, h); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f$ -> g -> uncurryF8(f).f(a, b, c, d, e, f$, g, h); } } diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index d5e80951..241bcefb 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -2,14 +2,7 @@ import static fj.Function.compose; -import fj.data.Array; -import fj.data.Either; -import fj.data.List; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Stream; -import fj.data.Tree; -import fj.data.Validation; +import fj.data.*; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; @@ -17,11 +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; @@ -46,8 +41,8 @@ public int hash(final A a) { * @param g The function to map. * @return A new hash. */ - public Hash comap(final F g) { - return new Hash(compose(f, g)); + public Hash contramap(final F g) { + return hash(compose(f, g)); } /** @@ -57,7 +52,7 @@ public Hash comap(final F g) { * @return A hash that uses the given function. */ public static Hash hash(final F f) { - return new Hash(f); + return new Hash<>(f); } /** @@ -66,11 +61,7 @@ public static Hash hash(final F f) { * @return A hash that uses {@link Object#hashCode()}. */ public static Hash anyHash() { - return new Hash(new F() { - public Integer f(final A a) { - return a.hashCode(); - } - }); + return hash(Object::hashCode); } /** @@ -113,6 +104,21 @@ public Integer f(final A a) { */ public static final Hash shortHash = anyHash(); + /** + * A hash instance for the BigInteger type. + */ + public static final Hash bigintHash = anyHash(); + + /** + * A hash instance for the BigDecimal type. + */ + 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. */ @@ -121,31 +127,27 @@ public Integer f(final A a) { /** * A hash instance for the {@link StringBuffer} type. */ - public static final Hash stringBufferHash = new Hash(new F() { - public Integer f(final StringBuffer sb) { + public static final Hash stringBufferHash = hash(sb -> { final int p = 419; int r = 239; for (int i = 0; i < sb.length(); i++) - r = p * r + sb.charAt(i); + r = p * r + sb.charAt(i); return r; - } }); /** * A hash instance for the {@link StringBuilder} type. */ - public static final Hash stringBuilderHash = new Hash(new F() { - public Integer f(final StringBuilder sb) { + public static final Hash stringBuilderHash = hash(sb -> { final int p = 419; int r = 239; for (int i = 0; i < sb.length(); i++) - r = p * r + sb.charAt(i); + r = p * r + sb.charAt(i); return r; - } }); /** @@ -156,10 +158,30 @@ public Integer f(final StringBuilder sb) { * @return A hash instance for the {@link Either} type. */ public static Hash> eitherHash(final Hash ha, final Hash hb) { - return new Hash>(new F, Integer>() { - public Integer f(final Either e) { - return e.isLeft() ? ha.hash(e.left().value()) : hb.hash(e.right().value()); - } + 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; }); } @@ -171,7 +193,7 @@ public Integer f(final Either e) { * @return A hash instance for the {@link Validation} type. */ public static Hash> validationHash(final Hash ha, final Hash hb) { - return eitherHash(ha, hb).comap(Validation.either()); + return eitherHash(ha, hb).contramap(Validation.either()); } /** @@ -181,19 +203,17 @@ public static Hash> validationHash(final Hash ha, fin * @return A hash instance for the {@link List} type. */ public static Hash> listHash(final Hash ha) { - return new Hash>(new F, Integer>() { - public Integer f(final List as) { + return hash(as -> { final int p = 419; int r = 239; List aas = as; while (!aas.isEmpty()) { - r = p * r + ha.hash(aas.head()); - aas = aas.tail(); + r = p * r + ha.hash(aas.head()); + aas = aas.tail(); } return r; - } }); } @@ -204,7 +224,7 @@ public Integer f(final List as) { * @return A hash instance for the {@link NonEmptyList} type. */ public static Hash> nonEmptyListHash(final Hash ha) { - return listHash(ha).comap(NonEmptyList.toList_()); + return listHash(ha).contramap(NonEmptyList.toList_()); } /** @@ -214,13 +234,17 @@ public static Hash> nonEmptyListHash(final Hash ha) { * @return A hash instance for the {@link Option} type. */ public static Hash> optionHash(final Hash ha) { - return new Hash>(new F, Integer>() { - public Integer f(final Option o) { - return o.isNone() ? 0 : ha.hash(o.some()); - } - }); + return hash(o -> o.isNone() ? 0 : ha.hash(o.some())); } + public static Hash> seqHash(final Hash h) { + return hash(s -> streamHash(h).hash(s.toStream())); + } + + public static Hash> setHash(final Hash h) { + return hash(s -> streamHash(h).hash(s.toStream())); + } + /** * A hash instance for the {@link Stream} type. * @@ -228,19 +252,17 @@ public Integer f(final Option o) { * @return A hash instance for the {@link Stream} type. */ public static Hash> streamHash(final Hash ha) { - return new Hash>(new F, Integer>() { - public Integer f(final Stream as) { + return hash(as -> { final int p = 419; int r = 239; Stream aas = as; while (!aas.isEmpty()) { - r = p * r + ha.hash(aas.head()); - aas = aas.tail()._1(); + r = p * r + ha.hash(aas.head()); + aas = aas.tail()._1(); } return r; - } }); } @@ -251,30 +273,76 @@ public Integer f(final Stream as) { * @return A hash instance for the {@link Array} type. */ public static Hash> arrayHash(final Hash ha) { - return new Hash>(new F, Integer>() { - public Integer f(final Array as) { + return hash(as -> { final int p = 419; int r = 239; for (int i = 0; i < as.length(); i++) { - r = p * r + ha.hash(as.get(i)); + r = p * r + ha.hash(as.get(i)); } return r; - } }); } /** + * 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).comap(Tree.flatten_()); + return streamHash(ha).contramap(Tree.flatten_()); } + public static Hash> treeMapHash(final Hash h, final Hash v) { + return hash(t -> streamHash(p2Hash(h, v)).hash(t.toStream())); + } + /** * A hash instance for a product-1. * @@ -282,7 +350,7 @@ public static Hash> treeHash(final Hash ha) { * @return A hash instance for a product-1. */ public static Hash> p1Hash(final Hash ha) { - return ha.comap(P1.__1()); + return ha.contramap(P1.__1()); } /** @@ -293,8 +361,7 @@ public static Hash> p1Hash(final Hash ha) { * @return A hash instance for a product-2. */ public static Hash> p2Hash(final Hash ha, final Hash hb) { - return new Hash>(new F, Integer>() { - public Integer f(final P2 p2) { + return hash(p2 -> { final int p = 419; int r = 239; @@ -302,7 +369,6 @@ public Integer f(final P2 p2) { r = p * r + hb.hash(p2._2()); return r; - } }); } @@ -315,8 +381,7 @@ public Integer f(final P2 p2) { * @return A hash instance for a product-3. */ public static Hash> p3Hash(final Hash ha, final Hash hb, final Hash hc) { - return new Hash>(new F, Integer>() { - public Integer f(final P3 p3) { + return hash(p3 -> { final int p = 419; int r = 239; @@ -325,7 +390,6 @@ public Integer f(final P3 p3) { r = p * r + hc.hash(p3._3()); return r; - } }); } @@ -340,18 +404,16 @@ public Integer f(final P3 p3) { */ public static Hash> p4Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd) { - return new Hash>(new F, Integer>() { - public Integer f(final P4 p4) { - final int p = 419; - int r = 239; + return hash(p4 -> { + final int p = 419; + int r = 239; - r = p * r + ha.hash(p4._1()); - r = p * r + hb.hash(p4._2()); - r = p * r + hc.hash(p4._3()); - r = p * r + hd.hash(p4._4()); + r = p * r + ha.hash(p4._1()); + r = p * r + hb.hash(p4._2()); + r = p * r + hc.hash(p4._3()); + r = p * r + hd.hash(p4._4()); - return r; - } + return r; }); } @@ -367,19 +429,17 @@ public Integer f(final P4 p4) { */ public static Hash> p5Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he) { - return new Hash>(new F, Integer>() { - public Integer f(final P5 p5) { - final int p = 419; - int r = 239; + return hash(p5 -> { + final int p = 419; + int r = 239; - r = p * r + ha.hash(p5._1()); - r = p * r + hb.hash(p5._2()); - r = p * r + hc.hash(p5._3()); - r = p * r + hd.hash(p5._4()); - r = p * r + he.hash(p5._5()); + r = p * r + ha.hash(p5._1()); + r = p * r + hb.hash(p5._2()); + r = p * r + hc.hash(p5._3()); + r = p * r + hd.hash(p5._4()); + r = p * r + he.hash(p5._5()); - return r; - } + return r; }); } @@ -397,20 +457,18 @@ public Integer f(final P5 p5) { public static Hash> p6Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he, final Hash hf) { - return new Hash>(new F, Integer>() { - public Integer f(final P6 p6) { - final int p = 419; - int r = 239; + return hash(p6 -> { + final int p = 419; + int r = 239; - r = p * r + ha.hash(p6._1()); - r = p * r + hb.hash(p6._2()); - r = p * r + hc.hash(p6._3()); - r = p * r + hd.hash(p6._4()); - r = p * r + he.hash(p6._5()); - r = p * r + hf.hash(p6._6()); + r = p * r + ha.hash(p6._1()); + r = p * r + hb.hash(p6._2()); + r = p * r + hc.hash(p6._3()); + r = p * r + hd.hash(p6._4()); + r = p * r + he.hash(p6._5()); + r = p * r + hf.hash(p6._6()); - return r; - } + return r; }); } @@ -430,21 +488,19 @@ public Integer f(final P6 p6) { final Hash hc, final Hash hd, final Hash he, final Hash hf, final Hash hg) { - return new Hash>(new F, Integer>() { - public Integer f(final P7 p7) { - final int p = 419; - int r = 239; + return hash(p7 -> { + final int p = 419; + int r = 239; - r = p * r + ha.hash(p7._1()); - r = p * r + hb.hash(p7._2()); - r = p * r + hc.hash(p7._3()); - r = p * r + hd.hash(p7._4()); - r = p * r + he.hash(p7._5()); - r = p * r + hf.hash(p7._6()); - r = p * r + hg.hash(p7._7()); + r = p * r + ha.hash(p7._1()); + r = p * r + hb.hash(p7._2()); + r = p * r + hc.hash(p7._3()); + r = p * r + hd.hash(p7._4()); + r = p * r + he.hash(p7._5()); + r = p * r + hf.hash(p7._6()); + r = p * r + hg.hash(p7._7()); - return r; - } + return r; }); } @@ -465,22 +521,20 @@ public Integer f(final P7 p7) { final Hash hc, final Hash hd, final Hash he, final Hash hf, final Hash hg, final Hash hh) { - return new Hash>(new F, Integer>() { - public Integer f(final P8 p8) { - final int p = 419; - int r = 239; + return hash(p8 -> { + final int p = 419; + int r = 239; - r = p * r + ha.hash(p8._1()); - r = p * r + hb.hash(p8._2()); - r = p * r + hc.hash(p8._3()); - r = p * r + hd.hash(p8._4()); - r = p * r + he.hash(p8._5()); - r = p * r + hf.hash(p8._6()); - r = p * r + hg.hash(p8._7()); - r = p * r + hh.hash(p8._8()); + r = p * r + ha.hash(p8._1()); + r = p * r + hb.hash(p8._2()); + r = p * r + hc.hash(p8._3()); + r = p * r + hd.hash(p8._4()); + r = p * r + he.hash(p8._5()); + r = p * r + hf.hash(p8._6()); + r = p * r + hg.hash(p8._7()); + r = p * r + hh.hash(p8._8()); - return r; - } + return r; }); } @@ -491,7 +545,7 @@ public Integer f(final P8 p8) { * @return A hash instance for a vector-2. */ public static Hash> v2Hash(final Hash ea) { - return streamHash(ea).comap(V2.toStream_()); + return streamHash(ea).contramap(V2.toStream_()); } /** @@ -501,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).comap(V3.toStream_()); + return streamHash(ea).contramap(V3.toStream_()); } /** @@ -511,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).comap(V4.toStream_()); + return streamHash(ea).contramap(V4.toStream_()); } /** @@ -521,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).comap(V5.toStream_()); + return streamHash(ea).contramap(V5.toStream_()); } /** @@ -531,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).comap(V6.toStream_()); + return streamHash(ea).contramap(V6.toStream_()); } /** @@ -541,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).comap(V7.toStream_()); + return streamHash(ea).contramap(V7.toStream_()); } /** @@ -551,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).comap(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 16962c15..cd918f5b 100644 --- a/core/src/main/java/fj/LcgRng.java +++ b/core/src/main/java/fj/LcgRng.java @@ -1,13 +1,11 @@ package fj; /** - * Created by MarkPerry on 7/07/2014. - * * https://en.wikipedia.org/wiki/Linear_congruential_generator */ public class LcgRng extends Rng { - private Long seed; + private final Long seed; public LcgRng() { this(System.currentTimeMillis()); @@ -17,14 +15,18 @@ public LcgRng(long s) { seed = s; } - public P2 nextInt() { + public final long getSeed() { + return seed; + } + + 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()); } @@ -36,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 cff6a556..bc05f33e 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1,14 +1,14 @@ package fj; -import static fj.Function.curry; -import static fj.Function.compose; -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 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; @@ -20,18 +20,94 @@ *
    *
  • Left Identity; forall x. sum(zero(), x) == x
  • *
  • Right Identity; forall x. sum(x, zero()) == x
  • - *
  • Associativity; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))
  • + *
  • 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 compose(m, P2.__1(), P2.__2(), P::p); } /** @@ -40,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))); + } + }); } /** @@ -51,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); } /** @@ -61,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); } /** @@ -70,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; } /** @@ -79,7 +229,22 @@ 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 {@code O(log n)} uses of + * {@link #sum(Object, Object)}. + * + * @param n multiplier + * @param a the value to be reapeatly summed + * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns + * {@code zero()} + */ + public A multiply(final int n, final A a) { + return def.multiply(n, a); } /** @@ -89,7 +254,7 @@ public A zero() { * @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()); } /** @@ -99,11 +264,7 @@ public A sumRight(final List as) { * @return The sum of the given values. */ public A sumRight(final Stream as) { - return as.foldRight(new F2, A>() { - public A f(final A a, final P1 ap1) { - return sum(a, ap1._1()); - } - }, zero); + return as.foldRight1(def::append, def.empty()); } /** @@ -113,7 +274,7 @@ public A f(final A a, final P1 ap1) { * @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()); } /** @@ -123,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); } /** @@ -132,11 +293,7 @@ public A sumLeft(final Stream as) { * @return a function that sums the given values with left-fold. */ public F, A> sumLeft() { - return new F, A>() { - public A f(final List as) { - return sumLeft(as); - } - }; + return this::sumLeft; } /** @@ -145,11 +302,7 @@ public A f(final List as) { * @return a function that sums the given values with right-fold. */ public F, A> sumRight() { - return new F, A>() { - public A f(final List as) { - return sumRight(as); - } - }; + return this::sumRight; } /** @@ -158,11 +311,7 @@ public A f(final List as) { * @return a function that sums the given values with left-fold. */ public F, A> sumLeftS() { - return new F, A>() { - public A f(final Stream as) { - return sumLeft(as); - } - }; + return this::sumLeft; } /** @@ -174,140 +323,468 @@ public A f(final Stream as) { */ public A join(final Iterable as, final A a) { final Stream s = iterableStream(as); - return s.isEmpty() ? - zero : - s.foldLeft1(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. + * + * Java 8+ users: use {@link #monoidDef(Semigroup.Definition, Object)} instead. * - * @param s The semigroup for the monoid. + * @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. @@ -316,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)); + } + }); } /** @@ -325,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))); + } + }); } /** @@ -343,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); + } + }); } /** @@ -352,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; + } + }); } /** @@ -361,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()); + } + }); } /** @@ -369,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 sets. + * 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 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 8ecccfd0..2bf90450 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -1,30 +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 fj.data.*; -import java.math.BigDecimal; -import java.math.BigInteger; +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)); } /** @@ -33,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; } /** @@ -44,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); } /** @@ -55,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; } /** @@ -64,11 +172,7 @@ public boolean eq(final A a1, final A a2) { * @return An Equal for this order. */ public Equal equal() { - return Equal.equal(curry(new F2() { - public Boolean f(final A a1, final A a2) { - return eq(a1, a2); - } - })); + return Equal.equalDef(def); } /** @@ -77,8 +181,8 @@ public Boolean f(final A a1, final A a2) { * @param f The function to map. * @return A new ord. */ - public Ord comap(final F f) { - return ord(F1Functions.o(F1Functions.o(F1Functions.andThen(f), this.f), f)); + public Ord contramap(final F f) { + return ordDef(contramapDef(f, def)); } /** @@ -91,9 +195,22 @@ public Ord comap(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. @@ -104,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; } /** @@ -114,11 +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 new F() { - public Boolean f(final A a2) { - return compare(a2, a) == Ordering.LT; - } - }; + return compose(o -> o == Ordering.GT, def.compare(a)); } /** @@ -128,11 +241,7 @@ public Boolean f(final A a2) { * @return A function that returns true if its argument is greater than the argument to this method. */ public F isGreaterThan(final A a) { - return new F() { - public Boolean f(final A a2) { - return compare(a2, a) == Ordering.GT; - } - }; + return compose(o -> o == Ordering.LT, def.compare(a)); } /** @@ -143,7 +252,7 @@ public Boolean f(final A a2) { * @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); } @@ -160,241 +269,186 @@ public A min(final A a1, final A a2) { /** * A function that returns the greater of its two arguments. + * */ - public final F> max = curry(new F2() { - public A f(final A a, final A a1) { - return max(a, a1); - } - }); + public final F> max; /** * A function that returns the lesser of its two arguments. */ - public final F> min = curry(new F2() { - public A f(final A a, final A a1) { - return min(a, a1); - } - }); + public final F> min; + + public final Semigroup minSemigroup() { + return semigroupDef(def.dual()); + } + + 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 = new Ord( - new F>() { - public F f(final Boolean a1) { - return new F() { - public Ordering f(final Boolean 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 = new Ord( - new F>() { - public F f(final Byte a1) { - return new F() { - public Ordering f(final Byte 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 = new Ord( - new F>() { - public F f(final Character a1) { - return new F() { - public Ordering f(final Character 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 = new Ord( - new F>() { - public F f(final Double a1) { - return new F() { - public Ordering f(final Double 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 = new Ord( - new F>() { - public F f(final Float a1) { - return new F() { - public Ordering f(final Float 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 = new Ord( - new F>() { - public F f(final Integer a1) { - return new F() { - public Ordering f(final Integer 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 = new Ord( - new F>() { - public F f(final BigInteger a1) { - return new F() { - public Ordering f(final BigInteger 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 = new Ord( - new F>() { - public F f(final BigDecimal a1) { - return new F() { - public Ordering f(final BigDecimal 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 = new Ord( - new F>() { - public F f(final Long a1) { - return new F() { - public Ordering f(final Long 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 = new Ord( - new F>() { - public F f(final Short a1) { - return new F() { - public Ordering f(final Short 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 = new Ord(curry(new F2() { - public Ordering f(final Ordering o1, final Ordering o2) { - return o1 == o2 ? - Ordering.EQ : - o1 == Ordering.LT ? - Ordering.LT : - o2 == Ordering.LT ? - Ordering.GT : - o1 == Ordering.EQ ? - Ordering.LT : - Ordering.GT; - } - })); + public static final Ord orderingOrd = ordDef((o1, o2) -> o1 == o2 ? + Ordering.EQ : + o1 == Ordering.LT ? + Ordering.LT : + o2 == Ordering.LT ? + Ordering.GT : + o1 == Ordering.EQ ? + Ordering.LT : + Ordering.GT); /** * An order instance for the {@link String} type. */ - public static final Ord stringOrd = new Ord( - new F>() { - public F f(final String a1) { - return new F() { - public Ordering f(final String 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 = - new Ord(new F>() { - public F f(final StringBuffer a1) { - return new F() { - public Ordering f(final StringBuffer a2) { - return 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 = - new Ord(new F>() { - public F f(final StringBuilder a1) { - return new F() { - public Ordering f(final StringBuilder a2) { - return stringOrd.compare(a1.toString(), a2.toString()); - } - }; - } - }); + public static final Ord stringBuilderOrd = stringOrd.contramap(StringBuilder::toString); /** * An order instance for the {@link Option} type. @@ -403,21 +457,14 @@ public Ordering f(final StringBuilder a2) { * @return An order instance for the {@link Option} type. */ public static Ord> optionOrd(final Ord oa) { - return new Ord>(new F, F, Ordering>>() { - public F, Ordering> f(final Option o1) { - return new F, Ordering>() { - public Ordering f(final Option o2) { - return o1.isNone() ? - o2.isNone() ? - Ordering.EQ : - Ordering.LT : - o2.isNone() ? - Ordering.GT : - oa.f.f(o1.some()).f(o2.some()); - } - }; - } - }); + Definition oaDef = oa.def; + return ordDef((o1, o2) -> o1.isNone() ? + o2.isNone() ? + Ordering.EQ : + Ordering.LT : + o2.isNone() ? + Ordering.GT : + oaDef.compare(o1.some()).f(o2.some())); } /** @@ -428,21 +475,15 @@ public Ordering f(final Option o2) { * @return An order instance for the {@link Either} type. */ public static Ord> eitherOrd(final Ord oa, final Ord ob) { - return new Ord>(new F, F, Ordering>>() { - public F, Ordering> f(final Either e1) { - return new F, Ordering>() { - public Ordering f(final Either e2) { - return e1.isLeft() ? - e2.isLeft() ? - oa.f.f(e1.left().value()).f(e2.left().value()) : - Ordering.LT : - e2.isLeft() ? - Ordering.GT : - ob.f.f(e1.right().value()).f(e2.right().value()); - } - }; - } - }); + Definition oaDef = oa.def; + Definition obDef = ob.def; + return ordDef((e1, e2) -> e1.isLeft() ? + e2.isLeft() ? + oaDef.compare(e1.left().value()).f(e2.left().value()) : + Ordering.LT : + e2.isLeft() ? + Ordering.GT : + obDef.compare(e1.right().value()).f(e2.right().value())); } /** @@ -453,7 +494,7 @@ public Ordering f(final Either e2) { * @return An order instance for the {@link Validation} type. */ public static Ord> validationOrd(final Ord oa, final Ord ob) { - return eitherOrd(oa, ob).comap(Validation.either()); + return eitherOrd(oa, ob).contramap(Validation.either()); } /** @@ -463,20 +504,56 @@ 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 new Ord>(new F, F, Ordering>>() { - public F, Ordering> f(final List l1) { - return new F, Ordering>() { - public Ordering f(final List l2) { - if (l1.isEmpty()) - return l2.isEmpty() ? Ordering.EQ : Ordering.LT; - else if (l2.isEmpty()) - return l1.isEmpty() ? Ordering.EQ : Ordering.GT; - else { - final Ordering c = oa.compare(l1.head(), l2.head()); - return c == Ordering.EQ ? listOrd(oa).f.f(l1.tail()).f(l2.tail()) : c; - } - } - }; + return ordDef((l1, l2) -> { + List x1 = l1; + List x2 = l2; + + while (x1.isNotEmpty() && x2.isNotEmpty()) { + final Ordering o = oa.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; + } + }); + } + + /** + * 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; } }); } @@ -488,7 +565,7 @@ else if (l2.isEmpty()) * @return An order instance for the {@link NonEmptyList} type. */ public static Ord> nonEmptyListOrd(final Ord oa) { - return listOrd(oa).comap(NonEmptyList.toList_()); + return listOrd(oa).contramap(NonEmptyList.toList_()); } /** @@ -498,21 +575,16 @@ public static Ord> nonEmptyListOrd(final Ord oa) { * @return An order instance for the {@link Stream} type. */ public static Ord> streamOrd(final Ord oa) { - return new Ord>(new F, F, Ordering>>() { - public F, Ordering> f(final Stream s1) { - return new F, Ordering>() { - public Ordering f(final Stream 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; - } - } - }; - } + 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()); + // FIXME: not stack safe + return c == Ordering.EQ ? streamOrd(oa).def.compare(s1.tail()._1()).f(s2.tail()._1()) : c; + } }); } @@ -523,27 +595,21 @@ else if (s2.isEmpty()) * @return An order instance for the {@link Array} type. */ public static Ord> arrayOrd(final Ord oa) { - return new Ord>(new F, F, Ordering>>() { - public F, Ordering> f(final Array a1) { - return new F, Ordering>() { - public Ordering f(final Array a2) { - int i = 0; - //noinspection ForLoopWithMissingComponent - for (; i < a1.length() && i < a2.length(); i++) { - final Ordering c = oa.compare(a1.get(i), a2.get(i)); - if (c == Ordering.GT || c == Ordering.LT) + return ordDef((a1, a2) -> { + int i = 0; + //noinspection ForLoopWithMissingComponent + for (; i < a1.length() && i < a2.length(); i++) { + final Ordering c = oa.compare(a1.get(i), a2.get(i)); + if (c == Ordering.GT || c == Ordering.LT) return c; - } - return i == a1.length() ? - i == a2.length() ? - Ordering.EQ : - Ordering.LT : - i == a1.length() ? - Ordering.EQ : - Ordering.GT; - } - }; - } + } + return i == a1.length() ? + i == a2.length() ? + Ordering.EQ : + Ordering.LT : + i == a1.length() ? + Ordering.EQ : + Ordering.GT; }); } @@ -554,21 +620,13 @@ public Ordering f(final Array a2) { * @return An order instance for the {@link Set} type. */ public static Ord> setOrd(final Ord oa) { - return streamOrd(oa).comap(new F, Stream>() { - public Stream f(final Set as) { - return as.toStream(); - } - }); + return streamOrd(oa).contramap(Set::toStream); } /** * An order instance for the {@link Unit} type. */ - public static final Ord unitOrd = ord(curry(new F2() { - public Ordering f(final Unit u1, final Unit u2) { - return Ordering.EQ; - } - })); + public static final Ord unitOrd = ordDef((u1, u2) -> Ordering.EQ); /** * An order instance for a product-1. @@ -577,7 +635,7 @@ public Ordering f(final Unit u1, final Unit u2) { * @return An order instance for a product-1. */ public static Ord> p1Ord(final Ord oa) { - return oa.comap(P1.__1()); + return oa.contramap(P1.__1()); } @@ -589,13 +647,17 @@ 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(new F2, P2, Ordering>() { - public Ordering f(final P2 a, final P2 b) { - return 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 on(P2.__1(), oa).ord(); + } + + public static Ord> p2Ord2(Ord ob) { + return on(P2.__2(), ob).ord(); + } + /** * An order instance for a product-3, with the first factor considered most significant. * @@ -605,19 +667,13 @@ public Ordering f(final P2 a, final P2 b) { * @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(new F2, P3, Ordering>() { - public Ordering f(final P3 a, final P3 b) { - return 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(); } /** * An order instance for the Natural type. */ - public static final Ord naturalOrd = bigintOrd.comap(Natural.bigIntegerValue); + public static final Ord naturalOrd = bigintOrd.contramap(Natural.bigIntegerValue); /** @@ -626,61 +682,17 @@ public Ordering f(final P3 a, final P3 b) { * @return An order instance for the Comparable interface. */ public static > Ord comparableOrd() { - return ord(new F>() { - public F f(final A a1) { - return new F() { - public Ordering f(final A a2) { - final int x = a1.compareTo(a2); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - } - }; - } - }); + return ordDef((a1, a2) -> Ordering.fromInt(a1.compareTo(a2))); } - /** - * An order instance that uses {@link Object#hashCode()} for computing the order and equality, - * thus objects returning the same hashCode are considered to be equals (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. ord(new F>() { - @Override - public F f(final A a) { - return new F() { - @Override - public Ordering f(final A a2) { - final int x = a.hashCode() - a2.hashCode(); - return x < 0 ? Ordering.LT : x == 0 ? Ordering.EQ : Ordering.GT; - } - }; - } - }); + class OrdComparator implements Comparator { + @Override + public final int compare(A o1, A o2) { + return Ord.this.compare(o1, o2).toInt(); + } } - /** - * 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. ord(new F>() { - @Override - public F f(final A a) { - return new F() { - @Override - public Ordering f(final A a2) { - final int x = a.hashCode() - a2.hashCode(); - return x < 0 ? Ordering.LT : x == 0 && a.equals(a2) ? Ordering.EQ : Ordering.GT; - } - }; - } - }); + public Comparator toComparator() { + return new OrdComparator(); } - } diff --git a/core/src/main/java/fj/Ordering.java b/core/src/main/java/fj/Ordering.java index 1b510678..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 { /** @@ -20,5 +18,19 @@ public enum Ordering { /** * Greater than. */ - GT + GT; + + public int toInt() { return ordinal() - 1 ; } + + public Ordering reverse() { + switch (this) { + case LT: return GT; + case GT: return LT; + } + return EQ; + } + + public static Ordering fromInt(int cmp) { + return cmp == 0 ? EQ : cmp > 0 ? GT : LT; + } } diff --git a/core/src/main/java/fj/P.java b/core/src/main/java/fj/P.java index 9347c060..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,11 +16,7 @@ private P() { * @return A function that puts an element in a product-1. */ public static F> p1() { - return new F>() { - public P1 f(final A a) { - return p(a); - } - }; + return P::p; } /** @@ -33,222 +27,263 @@ public P1 f(final A a) { */ public static P1 p(final A a) { return new P1() { - public A _1() { + @Override public A _1() { return a; } + @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); + } + + /** + * 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); + } - public static P1 lazy(final P1 pa) { - return pa; + /** + * 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); } - public static P2 lazy(final P1 pa, final P1 pb) { + /** + * 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 public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } }; } - public static P3 lazy(final P1 pa, final P1 pb, final P1 pc) { + public static P3 lazy(final F0 pa, final F0 pb, final F0 pc) { return new P3() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } }; } - public static P4 lazy(final P1 pa, final P1 pb, final P1 pc, final P1 pd) { + public static P4 lazy(final F0 pa, final F0 pb, final F0 pc, final F0 pd) { return new P4() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } @Override public D _4() { - return pd._1(); + return pd.f(); } }; } - public static P5 lazy(final P1 pa, final P1 pb, final P1 pc, final P1 pd, P1 pe) { + public static P5 lazy(final F0 pa, final F0 pb, final F0 pc, final F0 pd, F0 pe) { return new P5() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } @Override public D _4() { - return pd._1(); + return pd.f(); } @Override public E _5() { - return pe._1(); + return pe.f(); } }; } - public static P6 lazy(final P1 pa, final P1 pb, final P1 pc, final P1 pd, P1 pe, P1 pf) { + public static P6 lazy(final F0 pa, final F0 pb, final F0 pc, final F0 pd, F0 pe, F0 pf) { return new P6() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } @Override public D _4() { - return pd._1(); + return pd.f(); } @Override public E _5() { - return pe._1(); + return pe.f(); } @Override public F _6() { - return pf._1(); + return pf.f(); } }; } - public static P7 lazy(final P1 pa, final P1 pb, final P1 pc, final P1 pd, P1 pe, P1 pf, P1 pg) { + public static P7 lazy(final F0 pa, final F0 pb, final F0 pc, final F0 pd, F0 pe, F0 pf, F0 pg) { return new P7() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } @Override public D _4() { - return pd._1(); + return pd.f(); } @Override public E _5() { - return pe._1(); + return pe.f(); } @Override public F _6() { - return pf._1(); + return pf.f(); } @Override public G _7() { - return pg._1(); + return pg.f(); } }; } - public static P8 lazy(final P1 pa, final P1 pb, final P1 pc, final P1 pd, P1 pe, P1 pf, P1 pg, P1 ph) { + public static P8 lazy(final F0 pa, final F0 pb, final F0 pc, final F0 pd, F0 pe, F0 pf, F0 pg, F0 ph) { return new P8() { @Override public A _1() { - return pa._1(); + return pa.f(); } @Override public B _2() { - return pb._1(); + return pb.f(); } @Override public C _3() { - return pc._1(); + return pc.f(); } @Override public D _4() { - return pd._1(); + return pd.f(); } @Override public E _5() { - return pe._1(); + return pe.f(); } @Override public F _6() { - return pf._1(); + return pf.f(); } @Override public G _7() { - return pg._1(); + return pg.f(); } @Override public H _8() { - return ph._1(); + return ph.f(); } }; } + public static P2 lazyProduct(F0> f) { + return lazy(() -> f.f()._1(), () -> f.f()._2()); + } + /** * A function that puts an element in a product-2. * * @return A function that puts an element in a product-2. */ public static F>> p2() { - return new F>>() { - public F> f(final A a) { - return new F>() { - public P2 f(final B b) { - return p(a, b); - } - }; - } - }; + return a -> b -> p(a, b); } /** @@ -271,24 +306,12 @@ public B _2() { } /** - * A function that puts an element in a product-3. + * A function that puts elements in a product-3. * - * @return A function that puts an element in a product-3. + * @return A function that puts elements in a product-3. */ public static F>>> p3() { - return 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); - } - }; - } - }; - } - }; + return a -> b -> c -> p(a, b, c); } /** @@ -321,23 +344,7 @@ public C _3() { * @return A function that puts an element in a product-4. */ public static F>>>> p4() { - return 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); - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> p(a, b, c, d); } /** @@ -375,27 +382,7 @@ public D _4() { * @return A function that puts an element in a product-5. */ public static F>>>>> p5() { - return 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); - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> p(a, b, c, d, e); } /** @@ -438,31 +425,7 @@ public E _5() { * @return A function that puts an element in a product-6. */ public static F>>>>>> p6() { - return 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f -> p(a, b, c, d, e, f); } /** @@ -510,35 +473,7 @@ public E _5() { * @return A function that puts an element in a product-7. */ public static F>>>>>>> p7() { - return 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f -> g -> p(a, b, c, d, e, f, g); } /** @@ -591,39 +526,7 @@ public G _7() { * @return A function that puts an element in a product-8. */ public static F>>>>>>>> p8() { - return 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; + return a -> b -> c -> d -> e -> f -> g -> h -> p(a, b, c, d, e, f, g, h); } /** @@ -676,25 +579,11 @@ public H _8() { } public static P1 lazy(F f) { - return new P1() { - @Override - public A _1() { - return f.f(unit()); - } - }; + return lazy(() -> f.f(unit())); } public static P2 lazy(F fa, F fb) { - return new P2() { - @Override - public A _1() { - return fa.f(unit()); - } - @Override - public B _2() { - return 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 dcdeb77a..5e575a4d 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -1,14 +1,19 @@ package fj; +import fj.data.*; + +import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; + +import static fj.P.p; -import fj.data.Array; -import fj.data.List; -import fj.data.Stream; -import fj.data.Validation; -import fj.function.Try0; +public abstract class P1 implements F0 { -public abstract class P1 { + @Override + public final A f() { + return _1(); + } /** * Access the first element of the product. @@ -23,40 +28,28 @@ public abstract class P1 { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return new F, A>() { - public A f(final P1 p) { - return p._1(); - } - }; + return P1::_1; } - /** - * Promote any function to a transformation between P1s. - * - * @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 new F, P1>() { - public P1 f(final P1 a) { - return a.map(f); - } - }; - } + /** + * Promote any function to a transformation between P1s. + * + * @param f A function to promote to a transformation between P1s. + * @return A function promoted to operate on P1s. + */ + public static F, P1> map_(final F f) { + return a -> a.map(f); + } - /** + /** * Binds the given function to the value in a product-1 with a final join. * * @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 new P1() { - public B _1() { - return f.f(self._1())._1(); - } - }; + return P.lazy(() -> f.f(self._1())._1()); } /** @@ -66,15 +59,7 @@ public B _1() { * @return A function whose result is wrapped in a P1. */ public static F> curry(final F f) { - return new F>() { - public P1 f(final A a) { - return new P1() { - public B _1() { - return f.f(a); - } - }; - } - }; + return a -> P.lazy(() -> f.f(a)); } /** @@ -83,13 +68,9 @@ public B _1() { * @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(new F, P1>() { - public P1 f(final F f) { - return fmap(f).f(self); - } - }); + return cf.bind(f -> map_(f).f(self)); } /** @@ -99,10 +80,17 @@ public P1 f(final F f) { * @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 final P1 bind(final P1 cb, final F2 f) { + return bind(cb, f.curry()); + } + /** * Joins a P1 of a P1 with a bind operation. * @@ -110,7 +98,7 @@ public P1 bind(final P1 cb, final F> 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()); } /** @@ -120,13 +108,13 @@ public static P1 join(final P1> a) { * @return A function of arity-2 promoted to map over P1s. */ public static F, F, P1>> liftM2(final F> f) { - return Function.curry(new F2, P1, P1>() { - public P1 f(final P1 pa, final P1 pb) { - return pa.bind(pb, f); - } - }); + return Function.curry((pa, pb) -> pa.bind(pb, f)); } + public final P1 liftM2(P1 pb, F2 f) { + return P.lazy(() -> f.f(_1(), pb._1())); + } + /** * Turns a List of P1s into a single P1 of a List. * @@ -134,7 +122,7 @@ public P1 f(final P1 pa, final P1 pb) { * @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())); } /** @@ -143,11 +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 new F>, P1>>() { - public P1> f(final List> as) { - return sequence(as); - } - }; + return P1::sequence; } /** @@ -157,9 +141,16 @@ public P1> f(final List> as) { * @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(P1::_1)); + } + /** * Turns an array of P1s into a single P1 of an array. * @@ -167,11 +158,57 @@ public static P1> sequence(final Stream> as) { * @return A single P1 for the given array. */ public static P1> sequence(final Array> as) { - return new P1>() { - public Array _1() { - return as.map(P1.__1()); - } - }; + return P.lazy(() -> as.map(P1.__1())); + } + + /** + * Traversable instance of P1 for List + * + * @param f The function that takes A and produces a List (non-deterministic result) + * @return A List of P1 + */ + public final List> traverseList(final F> f){ + return f.f(_1()).map(P::p); + } + + /** + * Traversable instance of P1 for Either + * + * @param f The function produces Either + * @return An Either of P1 + */ + public final Either> traverseEither(final F> f){ + return f.f(_1()).right().map(P::p); + } + + /** + * Traversable instance of P1 for Option + * + * @param f The function that produces Option + * @return An Option of P1 + */ + public final Option> traverseOption(final F> f){ + return f.f(_1()).map(P::p); + } + + /** + * Traversable instance of P1 for Validation + * + * @param f The function might produces Validation + * @return An Validation of P1 + */ + public final Validation> traverseValidation(final F> f){ + return f.f(_1()).map(P::p); + } + + /** + * Traversable instance of P1 for Stream + * + * @param f The function that produces Stream + * @return An Stream of P1 + */ + public final Stream> traverseStream(final F> f){ + return f.f(_1()).map(P::p); } /** @@ -180,59 +217,120 @@ public Array _1() { * @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 new P1() { - public X _1() { - return f.f(self._1()); - } - }; + return P.lazy(() -> f.f(self._1())); } /** - * Provides a memoising P1 that remembers its value. - * - * @return A P1 that calls this P1 once and remembers the value for subsequent calls. - */ - public P1 memo() { - final P1 self = this; - return new P1() { - private final Object latch = new Object(); - @SuppressWarnings({"InstanceVariableMayNotBeInitialized"}) - private volatile SoftReference v; - - public A _1() { - A a = v != null ? v.get() : null; - if (a == null) - synchronized (latch) { - if (v == null || v.get() == null) - a = self._1(); - v = new SoftReference(a); - } - return a; - } - }; + * Wrap the memoized value into a WeakReference. + */ + public final P1 memo() { + return weakMemo(); + } + + /** + * Returns a P1 that remembers its value. + * + * @return A P1 that calls this P1 once and remembers the value for subsequent calls. + */ + public P1 hardMemo() { return new Memo<>(this); } + + /** + * Like memo, but the memoized value is wrapped into a WeakReference + */ + public P1 weakMemo() { return new WeakReferenceMemo<>(this); } + + /** + * Like memo, but the memoized value is wrapped into a SoftReference + */ + public P1 softMemo() { return new SoftReferenceMemo<>(this); } + + static final class Memo extends P1 { + private volatile F0 fa; + private A value; + + Memo(F0 fa) { this.fa = fa; } + + @Override public final A _1() { + return (fa == null) ? value : computeValue(); } - static P1 memo(F f) { - return P.lazy(f).memo(); + private synchronized A computeValue() { + F0 fa = this.fa; + if (fa != null) { + value = fa.f(); + this.fa = null; + } + return value; + } + + @Override public P1 hardMemo() { return this; } + @Override public P1 softMemo() { return this; } + @Override public P1 weakMemo() { return this; } } - /** - * Returns a constant function that always uses this value. - * - * @return A constant function that always uses this value. - */ - public F constant() { + abstract static class ReferenceMemo extends P1 { + 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(); + } - return new F() { - public A f(final B b) { - return P1.this._1(); - } - }; + 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 p1._1(); } - public String toString() { + abstract Reference newReference(B ref); + } + + 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 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; } + } + + /** + * Returns a constant function that always uses this value. + * + * @return A constant function that always uses this value. + */ + public final F constant() { return Function.constant(_1()); } + + @Override + public final String toString() { return Show.p1Show(Show.anyShow()).showS(this); } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(P1.class, this, other, () -> Equal.p1Equal(Equal.anyEqual())); + } + + @Override + 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 25eeed7d..b4979f03 100644 --- a/core/src/main/java/fj/P2.java +++ b/core/src/main/java/fj/P2.java @@ -1,13 +1,14 @@ package fj; import static fj.Function.*; -import fj.data.List; -import fj.data.Stream; +import static fj.P.weakMemo; +import static fj.data.optic.PLens.pLens; +import fj.data.*; +import fj.data.optic.Lens; +import fj.data.optic.PLens; /** * A product-2. - * - * @version %build.number% */ public abstract class P2 { /** @@ -24,21 +25,23 @@ public abstract class P2 { */ public abstract B _2(); + @Override + public final boolean equals(Object other) { + return Equal.equals0(P2.class, this, other, () -> Equal.p2Equal(Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.p2Hash(Hash.anyHash(), Hash.anyHash()).hash(this); + } + /** * Swaps the elements around in this product. * * @return A new product-2 with the elements swapped. */ public final P2 swap() { - return new P2() { - public B _1() { - return P2.this._2(); - } - - public A _2() { - return P2.this._1(); - } - }; + return P.lazy(P2.this::_2, P2.this::_1); } /** @@ -48,15 +51,8 @@ public A _2() { * @return A product with the given function applied. */ public final P2 map1(final F f) { - return new P2() { - public X _1() { - return f.f(P2.this._1()); - } - - public B _2() { - return P2.this._2(); - } - }; + P2 self = this; + return P.lazy(() -> f.f(self._1()), self::_2); } /** @@ -66,15 +62,7 @@ public B _2() { * @return A product with the given function applied. */ public final P2 map2(final F f) { - return new P2() { - public A _1() { - return P2.this._1(); - } - - public X _2() { - return f.f(P2.this._2()); - } - }; + return P.lazy(P2.this::_1, () -> f.f(P2.this._2())); } @@ -100,16 +88,8 @@ public final P2 split(final F f, final F g) { * and with the second element intact. */ public final P2 cobind(final F, C> k) { - return new P2() { - - public C _1() { - return k.f(P2.this); - } - - public B _2() { - return P2.this._2(); - } - }; + P2 self = this; + return P.lazy(() -> k.f(self), self::_2); } /** @@ -146,6 +126,26 @@ public final List sequenceW(final List, C>> fs) { return cs.toList(); } + public final List> traverseList(final F> f) { + return f.f(_2()).map(x -> P.p(_1(), x)); + } + + public final Stream> traverseStream(final F> f) { + return f.f(_2()).map(x -> P.p(_1(), x)); + } + + public final IO> traverseIO(final F> f) { + return IOFunctions.map(f.f(_2()), x -> P.p(_1(), x)); + } + + public final Option> traverseOption(final F> f) { + return f.f(_2()).map(x -> P.p(_1(), x)); + } + + public final Either> traverseEither(final F> f) { + return f.f(_2()).right().map(x -> P.p(_1(), x)); + } + /** * Applies a stream of comonadic functions to this product, returning a stream of values. * @@ -154,12 +154,8 @@ public final List sequenceW(final List, C>> fs) { */ public final Stream sequenceW(final Stream, C>> fs) { return fs.isEmpty() - ? Stream.nil() - : Stream.cons(fs.head().f(this), new P1>() { - public Stream _1() { - return sequenceW(fs.tail()._1()); - } - }); + ? Stream.nil() + : Stream.cons(fs.head().f(this), () -> sequenceW(fs.tail()._1())); } /** @@ -168,7 +164,7 @@ public Stream _1() { * @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); } /** @@ -177,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. @@ -188,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(); @@ -209,11 +266,7 @@ public B _2() { * @return A function that splits a given product between the two given functions and combines their output. */ public static F, P2> split_(final F f, final F g) { - return new F, P2>() { - public P2 f(final P2 p) { - return p.split(f, g); - } - }; + return p -> p.split(f, g); } /** @@ -223,11 +276,7 @@ public P2 f(final P2 p) { * @return The given function, promoted to map the first element of products. */ public static F, P2> map1_(final F f) { - return new F, P2>() { - public P2 f(final P2 p) { - return p.map1(f); - } - }; + return p -> p.map1(f); } /** @@ -237,11 +286,7 @@ public P2 f(final P2 p) { * @return The given function, promoted to map the second element of products. */ public static F, P2> map2_(final F f) { - return new F, P2>() { - public P2 f(final P2 p) { - return p.map2(f); - } - }; + return p -> p.map2(f); } /** @@ -273,11 +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 new F, P2>() { - public P2 f(final P2 p) { - return p.swap(); - } - }; + return P2::swap; } /** @@ -286,11 +327,7 @@ public P2 f(final P2 p) { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return new F, A>() { - public A f(final P2 p) { - return p._1(); - } - }; + return P2::_1; } /** @@ -299,11 +336,7 @@ public A f(final P2 p) { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return new F, B>() { - public B f(final P2 p) { - return p._2(); - } - }; + return P2::_2; } /** @@ -313,11 +346,7 @@ public B f(final P2 p) { * @return The function, transformed to operate on on a product-2 */ public static F, C> tuple(final F> f) { - return new F, C>() { - public C f(final P2 p) { - return f.f(p._1()).f(p._2()); - } - }; + return p -> f.f(p._1()).f(p._2()); } /** @@ -337,15 +366,53 @@ public static F, C> tuple(final F2 f) { * @return The function, transformed to an uncurried function of arity-2. */ public static F2 untuple(final F, C> f) { - return new F2() { - public C f(final A a, final B b) { - return f.f(P.p(a, b)); - } - }; + return (a, b) -> f.f(P.p(a, b)); } - public String toString() { + + @Override + public final String toString() { return Show.p2Show(Show.anyShow(), Show.anyShow()).showS(this); } + /** + * Optic factory methods for a P2 + + */ + public static final class Optic { + + private Optic() { + throw new UnsupportedOperationException(); + } + + /** + * Polyomorphic lens targeted on _1. + */ + public static PLens, P2, A, C> _1p() { + return pLens(__1(), a -> p2 -> P.p(a, p2._2())); + } + + /** + * Monomorphic lens targeted on _1. + */ + public static Lens, A> _1() { + return new Lens<>(_1p()); + } + + /** + * Polyomorphic lens targeted on _2. + */ + public static PLens, P2, B, C> _2p() { + return pLens(__2(), b -> p2 -> P.p(p2._1(), b)); + } + + /** + * Monomorphic lens targeted on _1. + */ + 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 06babb9c..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,11 +204,7 @@ public C _3() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return new F, A>() { - public A f(final P3 p) { - return p._1(); - } - }; + return P3::_1; } /** @@ -165,11 +213,7 @@ public A f(final P3 p) { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return new F, B>() { - public B f(final P3 p) { - return p._2(); - } - }; + return P3::_2; } /** @@ -178,16 +222,23 @@ public B f(final P3 p) { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return new F, C>() { - public C f(final P3 p) { - return p._3(); - } - }; + return P3::_3; } - public String toString() { + @Override + public final String toString() { return Show.p3Show(Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } + @Override + public final boolean equals(Object other) { + return Equal.equals0(P3.class, this, other, + () -> Equal.p3Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + 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 5cfac53e..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,11 +256,7 @@ public D _4() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return new F, A>() { - public A f(final P4 p) { - return p._1(); - } - }; + return P4::_1; } /** @@ -225,11 +265,7 @@ public A f(final P4 p) { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return new F, B>() { - public B f(final P4 p) { - return p._2(); - } - }; + return P4::_2; } /** @@ -238,11 +274,7 @@ public B f(final P4 p) { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return new F, C>() { - public C f(final P4 p) { - return p._3(); - } - }; + return P4::_3; } /** @@ -251,15 +283,23 @@ public C f(final P4 p) { * @return A function that returns the fourth element of a product. */ public static F, D> __4() { - return new F, D>() { - public D f(final P4 p) { - return p._4(); - } - }; + return P4::_4; } - public String toString() { + @Override + public final String toString() { return Show.p4Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } + @Override + public final boolean equals(Object other) { + return Equal.equals0(P4.class, this, other, + () -> Equal.p4Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + 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 ed300f41..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,11 +308,7 @@ public E _5() { * @return A function that returns the first element of a product. */ public static F, A> __1() { - return new F, A>() { - public A f(final P5 p) { - return p._1(); - } - }; + return P5::_1; } /** @@ -291,11 +317,7 @@ public A f(final P5 p) { * @return A function that returns the second element of a product. */ public static F, B> __2() { - return new F, B>() { - public B f(final P5 p) { - return p._2(); - } - }; + return P5::_2; } /** @@ -304,11 +326,7 @@ public B f(final P5 p) { * @return A function that returns the third element of a product. */ public static F, C> __3() { - return new F, C>() { - public C f(final P5 p) { - return p._3(); - } - }; + return P5::_3; } /** @@ -317,11 +335,7 @@ public C f(final P5 p) { * @return A function that returns the fourth element of a product. */ public static F, D> __4() { - return new F, D>() { - public D f(final P5 p) { - return p._4(); - } - }; + return P5::_4; } /** @@ -330,16 +344,23 @@ public D f(final P5 p) { * @return A function that returns the fifth element of a product. */ public static F, E> __5() { - return new F, E>() { - public E f(final P5 p) { - return p._5(); - } - }; + return P5::_5; } - public String toString() { + @Override + public final String toString() { return Show.p5Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } + @Override + 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())); + } + + @Override + 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 772003c7..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,15 +420,24 @@ 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; } - public String toString() { + @Override + public final String toString() { return Show.p6Show(Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow(), Show.anyShow()).showS(this); } + + @Override + 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())); + } + + @Override + 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 7fa413e4..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,16 +501,23 @@ 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; } - public String toString() { + @Override + 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 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())); + } + + @Override + 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 9e471c7f..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,15 +593,23 @@ 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; } - public String toString() { + @Override + 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 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())); + } + + @Override + 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 52e8c73f..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() { @@ -15,65 +13,37 @@ private Primitive() { /** * A function that converts booleans to bytes. */ - public static final F Boolean_Byte = new F() { - public Byte f(final Boolean b) { - return (byte) (b ? 1 : 0); - } - }; + public static final F Boolean_Byte = b -> (byte) (b ? 1 : 0); /** * A function that converts booleans to characters. */ - public static final F Boolean_Character = new F() { - public Character f(final Boolean b) { - return (char) (b ? 1 : 0); - } - }; + public static final F Boolean_Character = b -> (char) (b ? 1 : 0); /** * A function that converts booleans to doubles. */ - public static final F Boolean_Double = new F() { - public Double f(final Boolean b) { - return b ? 1D : 0D; - } - }; + public static final F Boolean_Double = b -> b ? 1D : 0D; /** * A function that converts booleans to floats. */ - public static final F Boolean_Float = new F() { - public Float f(final Boolean b) { - return b ? 1F : 0F; - } - }; + public static final F Boolean_Float = b -> b ? 1F : 0F; /** * A function that converts booleans to integers. */ - public static final F Boolean_Integer = new F() { - public Integer f(final Boolean b) { - return b ? 1 : 0; - } - }; + public static final F Boolean_Integer = b -> b ? 1 : 0; /** * A function that converts booleans to longs. */ - public static final F Boolean_Long = new F() { - public Long f(final Boolean b) { - return b ? 1L : 0L; - } - }; + public static final F Boolean_Long = b -> b ? 1L : 0L; /** * A function that converts booleans to shorts. */ - public static final F Boolean_Short = new F() { - public Short f(final Boolean b) { - return (short) (b ? 1 : 0); - } - }; + public static final F Boolean_Short = b -> (short) (b ? 1 : 0); // END Boolean -> @@ -82,65 +52,37 @@ public Short f(final Boolean b) { /** * A function that converts bytes to booleans. */ - public static final F Byte_Boolean = new F() { - public Boolean f(final Byte b) { - return b != 0; - } - }; + public static final F Byte_Boolean = b -> b != 0; /** * A function that converts bytes to characters. */ - public static final F Byte_Character = new F() { - public Character f(final Byte b) { - return (char) (byte) b; - } - }; + public static final F Byte_Character = b -> (char) (byte) b; /** * A function that converts bytes to doubles. */ - public static final F Byte_Double = new F() { - public Double f(final Byte b) { - return (double) b; - } - }; + public static final F Byte_Double = b -> (double) b; /** * A function that converts bytes to floats. */ - public static final F Byte_Float = new F() { - public Float f(final Byte b) { - return (float) b; - } - }; + public static final F Byte_Float = b -> (float) b; /** * A function that converts bytes to integers. */ - public static final F Byte_Integer = new F() { - public Integer f(final Byte b) { - return (int) b; - } - }; + public static final F Byte_Integer = b -> (int) b; /** * A function that converts bytes to longs. */ - public static final F Byte_Long = new F() { - public Long f(final Byte b) { - return (long) b; - } - }; + public static final F Byte_Long = b -> (long) b; /** * A function that converts bytes to shorts. */ - public static final F Byte_Short = new F() { - public Short f(final Byte b) { - return (short) b; - } - }; + public static final F Byte_Short = b -> (short) b; // END Byte -> @@ -149,65 +91,37 @@ public Short f(final Byte b) { /** * A function that converts characters to booleans. */ - public static final F Character_Boolean = new F() { - public Boolean f(final Character c) { - return c != 0; - } - }; + public static final F Character_Boolean = c -> c != 0; /** * A function that converts characters to bytes. */ - public static final F Character_Byte = new F() { - public Byte f(final Character c) { - return (byte) (char) c; - } - }; + public static final F Character_Byte = c -> (byte) (char) c; /** * A function that converts characters to doubles. */ - public static final F Character_Double = new F() { - public Double f(final Character c) { - return (double) (char) c; - } - }; + public static final F Character_Double = c -> (double) (char) c; /** * A function that converts characters to floats. */ - public static final F Character_Float = new F() { - public Float f(final Character c) { - return (float) (char) c; - } - }; + public static final F Character_Float = c -> (float) (char) c; /** * A function that converts characters to integers. */ - public static final F Character_Integer = new F() { - public Integer f(final Character c) { - return (int) (char) c; - } - }; + public static final F Character_Integer = c -> (int) (char) c; /** * A function that converts characters to longs. */ - public static final F Character_Long = new F() { - public Long f(final Character c) { - return (long) (char) c; - } - }; + public static final F Character_Long = c -> (long) (char) c; /** * A function that converts characters to shorts. */ - public static final F Character_Short = new F() { - public Short f(final Character c) { - return (short) (char) c; - } - }; + public static final F Character_Short = c -> (short) (char) c; // END Character -> @@ -216,65 +130,37 @@ public Short f(final Character c) { /** * A function that converts doubles to booleans. */ - public static final F Double_Boolean = new F() { - public Boolean f(final Double d) { - return d != 0D; - } - }; + public static final F Double_Boolean = d -> d != 0D; /** * A function that converts doubles to bytes. */ - public static final F Double_Byte = new F() { - public Byte f(final Double d) { - return (byte) (double) d; - } - }; + public static final F Double_Byte = d -> (byte) (double) d; /** * A function that converts doubles to characters. */ - public static final F Double_Character = new F() { - public Character f(final Double d) { - return (char) (double) d; - } - }; + public static final F Double_Character = d -> (char) (double) d; /** * A function that converts doubles to floats. */ - public static final F Double_Float = new F() { - public Float f(final Double d) { - return (float) (double) d; - } - }; + public static final F Double_Float = d -> (float) (double) d; /** * A function that converts doubles to integers. */ - public static final F Double_Integer = new F() { - public Integer f(final Double d) { - return (int) (double) d; - } - }; + public static final F Double_Integer = d -> (int) (double) d; /** * A function that converts doubles to longs. */ - public static final F Double_Long = new F() { - public Long f(final Double d) { - return (long) (double) d; - } - }; + public static final F Double_Long = d -> (long) (double) d; /** * A function that converts doubles to shorts. */ - public static final F Double_Short = new F() { - public Short f(final Double d) { - return (short) (double) d; - } - }; + public static final F Double_Short = d -> (short) (double) d; // END Double -> @@ -283,65 +169,37 @@ public Short f(final Double d) { /** * A function that converts floats to booleans. */ - public static final F Float_Boolean = new F() { - public Boolean f(final Float f) { - return f != 0F; - } - }; + public static final F Float_Boolean = f -> f != 0F; /** * A function that converts floats to bytes. */ - public static final F Float_Byte = new F() { - public Byte f(final Float f) { - return (byte) (float) f; - } - }; + public static final F Float_Byte = f -> (byte) (float) f; /** * A function that converts floats to characters. */ - public static final F Float_Character = new F() { - public Character f(final Float f) { - return (char) (float) f; - } - }; + public static final F Float_Character = f -> (char) (float) f; /** * A function that converts floats to doubles. */ - public static final F Float_Double = new F() { - public Double f(final Float f) { - return (double) (float) f; - } - }; + public static final F Float_Double = f -> (double) (float) f; /** * A function that converts floats to integers. */ - public static final F Float_Integer = new F() { - public Integer f(final Float f) { - return (int) (float) f; - } - }; + public static final F Float_Integer = f -> (int) (float) f; /** * A function that converts floats to longs. */ - public static final F Float_Long = new F() { - public Long f(final Float f) { - return (long) (float) f; - } - }; + public static final F Float_Long = f -> (long) (float) f; /** * A function that converts floats to shorts. */ - public static final F Float_Short = new F() { - public Short f(final Float f) { - return (short) (float) f; - } - }; + public static final F Float_Short = f -> (short) (float) f; // END Float -> @@ -350,65 +208,37 @@ public Short f(final Float f) { /** * A function that converts integers to booleans. */ - public static final F Integer_Boolean = new F() { - public Boolean f(final Integer i) { - return i != 0; - } - }; + public static final F Integer_Boolean = i -> i != 0; /** * A function that converts integers to bytes. */ - public static final F Integer_Byte = new F() { - public Byte f(final Integer i) { - return (byte) (int) i; - } - }; + public static final F Integer_Byte = i -> (byte) (int) i; /** * A function that converts integers to characters. */ - public static final F Integer_Character = new F() { - public Character f(final Integer i) { - return (char) (int) i; - } - }; + public static final F Integer_Character = i -> (char) (int) i; /** * A function that converts integers to doubles. */ - public static final F Integer_Double = new F() { - public Double f(final Integer i) { - return (double) i; - } - }; + public static final F Integer_Double = i -> (double) i; /** * A function that converts integers to floats. */ - public static final F Integer_Float = new F() { - public Float f(final Integer i) { - return (float) i; - } - }; + public static final F Integer_Float = i -> (float) i; /** * A function that converts integers to longs. */ - public static final F Integer_Long = new F() { - public Long f(final Integer i) { - return (long) i; - } - }; + public static final F Integer_Long = i -> (long) i; /** * A function that converts integers to shorts. */ - public static final F Integer_Short = new F() { - public Short f(final Integer i) { - return (short) (int) i; - } - }; + public static final F Integer_Short = i -> (short) (int) i; // END Integer -> @@ -417,65 +247,37 @@ public Short f(final Integer i) { /** * A function that converts longs to booleans. */ - public static final F Long_Boolean = new F() { - public Boolean f(final Long l) { - return l != 0L; - } - }; + public static final F Long_Boolean = l -> l != 0L; /** * A function that converts longs to bytes. */ - public static final F Long_Byte = new F() { - public Byte f(final Long l) { - return (byte) (long) l; - } - }; + public static final F Long_Byte = l -> (byte) (long) l; /** * A function that converts longs to characters. */ - public static final F Long_Character = new F() { - public Character f(final Long l) { - return (char) (long) l; - } - }; + public static final F Long_Character = l -> (char) (long) l; /** * A function that converts longs to doubles. */ - public static final F Long_Double = new F() { - public Double f(final Long l) { - return (double) (long) l; - } - }; + public static final F Long_Double = l -> (double) (long) l; /** * A function that converts longs to floats. */ - public static final F Long_Float = new F() { - public Float f(final Long l) { - return (float) (long) l; - } - }; + public static final F Long_Float = l -> (float) (long) l; /** * A function that converts longs to integers. */ - public static final F Long_Integer = new F() { - public Integer f(final Long l) { - return (int) (long) l; - } - }; + public static final F Long_Integer = l -> (int) (long) l; /** * A function that converts longs to shorts. */ - public static final F Long_Short = new F() { - public Short f(final Long l) { - return (short) (long) l; - } - }; + public static final F Long_Short = l -> (short) (long) l; // END Long -> @@ -484,65 +286,37 @@ public Short f(final Long l) { /** * A function that converts shorts to booleans. */ - public static final F Short_Boolean = new F() { - public Boolean f(final Short s) { - return s != 0; - } - }; + public static final F Short_Boolean = s -> s != 0; /** * A function that converts shorts to bytes. */ - public static final F Short_Byte = new F() { - public Byte f(final Short s) { - return (byte) (short) s; - } - }; + public static final F Short_Byte = s -> (byte) (short) s; /** * A function that converts shorts to characters. */ - public static final F Short_Character = new F() { - public Character f(final Short s) { - return (char) (short) s; - } - }; + public static final F Short_Character = s -> (char) (short) s; /** * A function that converts shorts to doubles. */ - public static final F Short_Double = new F() { - public Double f(final Short s) { - return (double) (short) s; - } - }; + public static final F Short_Double = s -> (double) (short) s; /** * A function that converts shorts to floats. */ - public static final F Short_Float = new F() { - public Float f(final Short s) { - return (float) (short) s; - } - }; + public static final F Short_Float = s -> (float) (short) s; /** * A function that converts shorts to integers. */ - public static final F Short_Integer = new F() { - public Integer f(final Short s) { - return (int) (short) s; - } - }; + public static final F Short_Integer = s -> (int) (short) s; /** * A function that converts shorts to longs. */ - public static final F Short_Long = new F() { - public Long f(final Short s) { - return (long) (short) s; - } - }; + public static final F Short_Long = s -> (long) (short) s; // END Short } diff --git a/core/src/main/java/fj/Rng.java b/core/src/main/java/fj/Rng.java index 679be544..46c31b27 100644 --- a/core/src/main/java/fj/Rng.java +++ b/core/src/main/java/fj/Rng.java @@ -1,20 +1,18 @@ package fj; -/** - * Created by MarkPerry on 7/07/2014. - */ public abstract class Rng { public abstract P2 nextInt(); public abstract P2 nextLong(); - public P2 range(int low, int high) { + // [low, high] inclusive + 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 2b377cb5..cd31e1e3 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -1,31 +1,99 @@ package fj; -import static fj.Function.curry; - 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; import fj.data.Set; import fj.data.Stream; -import java.math.BigInteger; import java.math.BigDecimal; +import java.math.BigInteger; + +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,248 +123,347 @@ 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 {@code O(log n)} uses of + * {@link #sum(Object, Object)}. + * + * @param n multiplier + * @param a the value to be reapeatly summed n + 1 times + * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns + * {@code zero()} + */ + public A multiply1p(int n, A a) { + return def.multiply1p(n, a); + } + + + /** + * 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))); + } + + @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(new F2() { - public Integer f(final Integer i1, final Integer i2) { - return i1 + i2; - } - }); /** - * A semigroup that adds doubles. + * A semigroup that adds integers. */ - public static final Semigroup doubleAdditionSemigroup = semigroup(new F2() { - public Double f(final Double d1, final Double d2) { - return d1 + d2; - } - }); + public static final Semigroup intAdditionSemigroup = intAdditionMonoid.semigroup(); /** * A semigroup that multiplies integers. */ - public static final Semigroup intMultiplicationSemigroup = semigroup(new F2() { - public Integer f(final Integer i1, final Integer i2) { - return i1 * i2; - } - }); - - /** - * A semigroup that multiplies doubles. - */ - public static final Semigroup doubleMultiplicationSemigroup = semigroup(new F2() { - public Double f(final Double d1, final Double d2) { - return 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(new F2() { - public BigInteger f(final BigInteger i1, final BigInteger i2) { - return i1.add(i2); - } - }); + public static final Semigroup bigintAdditionSemigroup = bigintAdditionMonoid.semigroup(); /** * A semigroup that multiplies big integers. */ public static final Semigroup bigintMultiplicationSemigroup = - semigroup(new F2() { - public BigInteger f(final BigInteger i1, final BigInteger i2) { - return 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(new F2() { - public BigDecimal f(final BigDecimal i1, final BigDecimal i2) { - return i1.add(i2); - } - }); + public static final Semigroup bigdecimalAdditionSemigroup = bigdecimalAdditionMonoid.semigroup(); /** * A semigroup that multiplies big decimals. */ - public static final Semigroup bigdecimalMultiplicationSemigroup = - semigroup(new F2() { - public BigDecimal f(final BigDecimal i1, final BigDecimal i2) { - return 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(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return 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(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return 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(new F2() { - public Long f(final Long x, final Long y) { - return x + y; - } - }); + public static final Semigroup longAdditionSemigroup = longAdditionMonoid.semigroup(); /** * A semigroup that multiplies longs. */ - public static final Semigroup longMultiplicationSemigroup = semigroup(new F2() { - public Long f(final Long x, final Long y) { - return 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(new F2() { - public Boolean f(final Boolean b1, final Boolean b2) { - return b1 || b2; - } - }); + public static final Semigroup disjunctionSemigroup = disjunctionMonoid.semigroup(); /** * A semigroup that XORs booleans. */ - public static final Semigroup exclusiveDisjunctionSemiGroup = semigroup(new F2() { - public Boolean f(final Boolean p, final Boolean q) { - return p && !q || !p && q; - } - }); + public static final Semigroup exclusiveDisjunctionSemiGroup = exclusiveDisjunctionMonoid.semigroup(); /** * A semigroup that ANDs booleans. */ - public static final Semigroup conjunctionSemigroup = semigroup(new F2() { - public Boolean f(final Boolean b1, final Boolean b2) { - return b1 && b2; - } - }); + public static final Semigroup conjunctionSemigroup = conjunctionMonoid.semigroup(); /** * A semigroup that appends strings. */ - public static final Semigroup stringSemigroup = semigroup(new F2() { - public String f(final String s1, final String s2) { - return s1 + s2; - } - }); + public static final Semigroup stringSemigroup = stringMonoid.semigroup(); /** * A semigroup that appends string buffers. */ - public static final Semigroup stringBufferSemigroup = - semigroup(new F2() { - public StringBuffer f(final StringBuffer s1, final StringBuffer s2) { - return new StringBuffer(s1).append(s2); - } - }); + public static final Semigroup stringBufferSemigroup = stringBufferMonoid.semigroup(); /** * A semigroup that appends string builders. */ - public static final Semigroup stringBuilderSemigroup = - semigroup(new F2() { - public StringBuilder f(final StringBuilder s1, final StringBuilder s2) { - return 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. @@ -305,15 +472,8 @@ public StringBuilder f(final StringBuilder s1, final StringBuilder s2) { * @return A semigroup for functions. */ public static Semigroup> functionSemigroup(final Semigroup sb) { - return semigroup(new F2, F, F>() { - public F f(final F a1, final F a2) { - return new F() { - public B f(final A a) { - return 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))); } /** @@ -322,11 +482,7 @@ public B f(final A a) { * @return A semigroup for lists. */ public static Semigroup> listSemigroup() { - return semigroup(new F2, List, List>() { - public List f(final List a1, final List a2) { - return a1.append(a2); - } - }); + return Monoid.listMonoid().semigroup(); } /** @@ -335,21 +491,17 @@ public List f(final List a1, final List a2) { * @return A semigroup for non-empty lists. */ public static Semigroup> nonEmptyListSemigroup() { - return semigroup(new F2, NonEmptyList, NonEmptyList>() { - public NonEmptyList f(final NonEmptyList a1, final NonEmptyList a2) { + + return semigroupDef(new Definition>() { + @Override + public NonEmptyList append(NonEmptyList a1, NonEmptyList a2) { return a1.append(a2); } - }); - } - /** - * A semigroup for optional values. - ** @return A semigroup for optional values. - */ - public static Semigroup> optionSemigroup() { - return semigroup(new F2, Option, Option>() { - public Option f(final Option a1, final Option a2) { - return a1.isSome() ? a1 : 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); } }); } @@ -360,11 +512,7 @@ public Option f(final Option a1, final Option a2) { * @return A semigroup for optional values that take the first available value. */ public static Semigroup> firstOptionSemigroup() { - return semigroup(new F2, Option, Option>() { - public Option f(final Option a1, final Option a2) { - return a1.orElse(a2); - } - }); + return Monoid.firstOptionMonoid().semigroup(); } /** @@ -373,11 +521,7 @@ public Option f(final Option a1, final Option a2) { * @return A semigroup for optional values that take the last available value. */ public static Semigroup> lastOptionSemigroup() { - return semigroup(new F2, Option, Option>() { - public Option f(final Option a1, final Option a2) { - return a2.orElse(a1); - } - }); + return Monoid.lastOptionMonoid().semigroup(); } /** @@ -386,11 +530,7 @@ public Option f(final Option a1, final Option a2) { * @return A semigroup for streams. */ public static Semigroup> streamSemigroup() { - return semigroup(new F2, Stream, Stream>() { - public Stream f(final Stream a1, final Stream a2) { - return a1.append(a2); - } - }); + return Monoid.streamMonoid().semigroup(); } /** @@ -399,74 +539,75 @@ public Stream f(final Stream a1, final Stream a2) { * @return A semigroup for arrays. */ public static Semigroup> arraySemigroup() { - return semigroup(new F2, Array, Array>() { - public Array f(final Array a1, final Array a2) { - return 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(new F2, P1, P1>() { - public P1 f(final P1 a1, final P1 a2) { - return new P1() { - public A _1() { - return 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(new F2, P2, P2>() { - public P2 f(final P2 a1, final P2 a2) { - return new P2() { - public A _1() { - return sa.sum(a1._1(), a2._1()); - } + return semigroupDef((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); + } - public B _2() { - return 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(new F2() { - public Unit f(final Unit u1, final Unit u2) { - return 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(new F2, Set, Set>() { - public Set f(final Set a, final Set b) { - return 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 7351d918..8a21a281 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -1,15 +1,9 @@ package fj; -import fj.data.Array; -import fj.data.Either; -import fj.data.LazyString; -import fj.data.List; -import fj.data.Natural; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Stream; -import fj.data.Tree; -import fj.data.Validation; +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; @@ -18,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; @@ -28,13 +23,10 @@ import static fj.data.Stream.cons; import static fj.data.Stream.fromString; import static fj.data.Stream.join; -import static fj.data.Stream.nil; import static fj.data.Stream.single; /** * Renders an object for display. - * - * @version %build.number% */ public final class Show { private final F> f; @@ -49,7 +41,7 @@ private Show(final F> f) { * @param f The function to map. * @return A new show. */ - public Show comap(final F f) { + public Show contramap(final F f) { return show(compose(this.f, f)); } @@ -89,11 +81,7 @@ public String showS(final A a) { * @return the transformation equivalent to this show. */ public F showS_() { - return new F() { - public String f(final A a) { - return showS(a); - } - }; + return this::showS; } /** @@ -154,7 +142,7 @@ public void printlnE(final A a) { * @return A show instance. */ public static Show show(final F> f) { - return new Show(f); + return new Show<>(f); } /** @@ -164,11 +152,7 @@ public static Show show(final F> f) { * @return A show instance. */ public static Show showS(final F f) { - return new Show(new F>() { - public Stream f(final A a) { - return fromString(f.f(a)); - } - }); + return show(a -> fromString(f.f(a))); } /** @@ -177,11 +161,7 @@ public Stream f(final A a) { * @return A show instance that uses {@link Object#toString()} to perform the display rendering. */ public static Show anyShow() { - return new Show(new F>() { - public Stream f(final A a) { - return Stream.fromString((a == null) ? "null" : a.toString()); - } - }); + return show(a -> fromString((a == null) ? "null" : a.toString())); } /** @@ -256,13 +236,9 @@ public Stream f(final A a) { * @return A show instance for the {@link Option} type. */ public static Show> optionShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final Option o) { - return o.isNone() ? - fromString("None") : - fromString("Some(").append(sa.f.f(o.some())).append(single(')')); - } - }); + return show(o -> o.isNone() ? + fromString("None") : + fromString("Some(").append(sa.f.f(o.some())).append(single(')'))); } /** @@ -273,13 +249,32 @@ public Stream f(final Option o) { * @return A show instance for the {@link Either} type. */ public static Show> eitherShow(final Show sa, final Show sb) { - return new Show>(new F, Stream>() { - public Stream f(final Either e) { - return e.isLeft() ? - fromString("Left(").append(sa.f.f(e.left().value())).append(single(')')) : - fromString("Right(").append(sb.f.f(e.right().value())).append(single(')')); - } - }); + return show(e -> e.isLeft() ? + fromString("Left(").append(sa.f.f(e.left().value())).append(single(')')) : + 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(')'))); } /** @@ -290,13 +285,9 @@ public Stream f(final Either e) { * @return A show instance for the {@link Validation} type. */ public static Show> validationShow(final Show sa, final Show sb) { - return new Show>(new F, Stream>() { - public Stream f(final Validation v) { - return v.isFail() ? - fromString("Fail(").append(sa.f.f(v.fail())).append(single(')')) : - fromString("Success(").append(sb.f.f(v.success())).append(single(')')); - } - }); + return show(v -> v.isFail() ? + fromString("Fail(").append(sa.f.f(v.fail())).append(single(')')) : + fromString("Success(").append(sb.f.f(v.success())).append(single(')'))); } /** @@ -306,11 +297,7 @@ public Stream f(final Validation v) { * @return A show instance for the {@link Stream} type. */ public static Show> listShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final List as) { - return streamShow(sa).show(as.toStream()); - } - }); + return show(as -> streamShow(sa, "List(", ",", ")").show(as.toStream())); } /** @@ -320,7 +307,7 @@ public Stream f(final List as) { * @return A show instance for the {@link NonEmptyList} type. */ public static Show> nonEmptyListShow(final Show sa) { - return listShow(sa).comap(NonEmptyList.toList_()); + return listShow(sa).contramap(NonEmptyList.toList_()); } /** @@ -330,16 +317,106 @@ public static Show> nonEmptyListShow(final Show sa) { * @return A show instance for the {@link Tree} type. */ public static Show> treeShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final Tree a) { - final Stream b = sa.f.f(a.root()) - .append(p1Show(streamShow(treeShow(sa))).f.f(a.subForest())) - .snoc(')'); - return cons('(', p(b)); + return show(a -> { + Stream result = sa.f.f(a.root()); + if (!a.subForest()._1().isEmpty()) { + result = result.append(fromString(",")).append(streamShow(treeShow(sa), "", ",", "").f.f(a.subForest()._1())); } + return fromString("Tree(").append(p(result)).append(fromString(")")); + }); + } + + 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())); + } + + /** + * A show instance for the {@link Set} type. + * + * @param sa Show for the elements of the set. + * @return A show instance for the {@link Set} type. + */ + public static Show> setShow(final Show sa) { + return show(s -> streamShow(sa, "Set(", ",", ")").show(s.toStream())); + } + + /** + * A show instance for the {@link TreeMap} type. + * + * @param sk Show for the keys of the TreeMap. + * @param sv Show for the values of the TreeMap. + * @return A show instance for the {@link TreeMap} type. + */ + public static Show> treeMapShow(final Show sk, final Show sv) { + return show(tm -> { + Stream> stream = Stream.iteratorStream(tm.iterator()); + return streamShow(p2MapShow(sk, sv), "TreeMap(", ",", ")").show(stream); + }); + } + + /** + * A show instance for the {@link P2 tuple-2} type in the style of a mapping from A to B. + * + * @param sa Show for the first element of the tuple. + * @param sb Show for the second element of the tuple. + * @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, "(", ": ", ")"); + } + + /** + * A show instance for the {@link P2 tuple-2} type. + * + * @param sa Show for the first element of the tuple. + * @param sb Show for the second element of the tuple. + * @param start Prefix string for the show. + * @param sep Separator string between elements of the tuple. + * @param end Suffix string for the show. + * @return A show instance for the {@link P2 tuple-2} type. + */ + public static Show> p2Show(final Show sa, final Show sb, String start, String sep, String end) { + return show(p -> fromString(start).append(p(sa.show(p._1()))).append(fromString(sep)).append(sb.show(p._2())).append(fromString(end))); + } + /** * A show instance for the {@link Stream} type. * @@ -347,11 +424,27 @@ public Stream f(final Tree a) { * @return A show instance for the {@link Stream} type. */ public static Show> streamShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final Stream as) { - return join(as.map(sa.show_()).intersperse(fromString(",")).cons(fromString("<")).snoc(p(fromString(">")))); - } - }); + return streamShow(sa, "Stream(", ",", ")"); + } + + /** + * A show instance for the {@link Stream} type. + * + * @param sa Show for the first element of the tuple. + * @param start Prefix string for the show. + * @param sep Separator string between elements of the stream. + * @param end Suffix string for the show. + * @return A show instance for the {@link Stream} type. + */ + public static Show> streamShow(final Show sa, String start, String sep, String end) { + return show(streamShow_(sa, start, sep, end)); + } + + /** + * Returns the transformation equivalent for the stream show. + */ + public static F, Stream> streamShow_(final Show sa, String start, String sep, String end) { + return as -> join(as.map(sa.show_()).intersperse(fromString(sep)).cons(fromString(start)).snoc(p(fromString(end)))); } /** @@ -361,21 +454,17 @@ public Stream f(final Stream as) { * @return A show instance for the {@link Array} type. */ public static Show> arrayShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final Array as) { - Stream b = nil(); + return show(as -> { + Stream b = fromString("Array("); - for (int i = 0; i < as.length(); i++) { - b = b.append(sa.f.f(as.get(i))); + for (int i = 0; i < as.length(); i++) { + b = b.append(sa.f.f(as.get(i))); - if (i != as.length() - 1) - b = b.snoc(','); - } - - b = b.snoc('}'); - - return cons('{', p(b)); + if (i != as.length() - 1) + b = b.append(fromString(",")); } + + return b.append(fromString(")")); }); } @@ -385,11 +474,7 @@ public Stream f(final Array as) { * @return A show instance for the {@link Class} type. */ public static Show> classShow() { - return new Show>(new F, Stream>() { - public Stream f(final Class c) { - return anyShow().show(c.clas()); - } - }); + return show(c -> anyShow().show(c.clas())); } /** @@ -399,11 +484,15 @@ public Stream f(final Class c) { * @return A show instance for the {@link P1 tuple-1} type. */ public static Show> p1Show(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final P1 p) { - return cons('(', p(sa.show(p._1()))).snoc(')'); - } - }); + return p1ShowLazy(sa); + } + + public static Show> p1ShowLazy(final Show sa) { + return show(p -> fromString("(?)")); + } + + public static Show> p1ShowEager(final Show sa) { + return show(p -> cons('(', p(sa.show(p._1()))).snoc(')')); } /** @@ -414,11 +503,7 @@ public Stream f(final P1 p) { * @return A show instance for the {@link P2 tuple-2} type. */ public static Show> p2Show(final Show sa, final Show sb) { - return new Show>(new F, Stream>() { - public Stream f(final P2 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(')'); - } - }); + return p2Show(sa, sb, "(", ",", ")"); } /** @@ -430,12 +515,8 @@ public Stream f(final P2 p) { * @return A show instance for the {@link P3 tuple-3} type. */ public static Show> p3Show(final Show sa, final Show sb, final Show sc) { - return new Show>(new F, Stream>() { - public Stream f(final P3 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(')'); - } - }); + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(')')); } /** @@ -449,12 +530,8 @@ public Stream f(final P3 p) { */ public static Show> p4Show(final Show sa, final Show sb, final Show sc, final Show sd) { - return new Show>(new F, Stream>() { - public Stream f(final P4 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(')'); - } - }); + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(')')); } /** @@ -469,12 +546,8 @@ public Stream f(final P4 p) { */ public static Show> p5Show(final Show sa, final Show sb, final Show sc, final Show sd, final Show se) { - return new Show>(new F, Stream>() { - public Stream f(final P5 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',').append(se.show(p._5())).snoc(')'); - } - }); + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',').append(se.show(p._5())).snoc(')')); } /** @@ -491,13 +564,9 @@ public Stream f(final P5 p) { public static Show> p6Show(final Show sa, final Show sb, final Show sc, final Show sd, final Show se, final Show sf) { - return new Show>(new F, Stream>() { - public Stream f(final P6 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') - .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(')'); - } - }); + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') + .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(')')); } /** @@ -516,13 +585,9 @@ public Stream f(final P6 p) { final Show sc, final Show sd, final Show se, final Show sf, final Show sg) { - return new Show>(new F, Stream>() { - public Stream f(final P7 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') - .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(',').append(sg.show(p._7())).snoc(')'); - } - }); + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') + .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(',').append(sg.show(p._7())).snoc(')')); } /** @@ -542,13 +607,15 @@ public Stream f(final P7 p) { final Show sc, final Show sd, final Show se, final Show sf, final Show sg, final Show sh) { - return new Show>(new F, Stream>() { - public Stream f(final P8 p) { - return cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') - .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') - .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(',') - .append(sg.show(p._7())).snoc(',').append(sh.show(p._8())).snoc(')'); - } + return show(p -> cons('(', p(sa.show(p._1()))).snoc(',').append(sb.show(p._2())).snoc(',') + .append(sc.show(p._3())).snoc(',').append(sd.show(p._4())).snoc(',') + .append(se.show(p._5())).snoc(',').append(sf.show(p._6())).snoc(',') + .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()); }); } @@ -559,7 +626,7 @@ public Stream f(final P8 p) { * @return A show instance for a vector-2. */ public static Show> v2Show(final Show ea) { - return streamShow(ea).comap(V2.toStream_()); + return streamShow(ea, "V2(", ",", ")").contramap(V2.toStream_()); } /** @@ -569,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).comap(V3.toStream_()); + return streamShow(ea, "V3(", ",", ")").contramap(V3.toStream_()); } /** @@ -579,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).comap(V4.toStream_()); + return streamShow(ea, "V4(", ",", ")").contramap(V4.toStream_()); } /** @@ -589,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).comap(V5.toStream_()); + return streamShow(ea, "V5(", ",", ")").contramap(V5.toStream_()); } /** @@ -599,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).comap(V6.toStream_()); + return streamShow(ea, "V6(", ",", ")").contramap(V6.toStream_()); } /** @@ -609,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).comap(V7.toStream_()); + return streamShow(ea, "V7(", ",", ")").contramap(V7.toStream_()); } /** @@ -619,17 +686,13 @@ 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).comap(V8.toStream_()); + return streamShow(ea, "V8(", ",", ")").contramap(V8.toStream_()); } /** * A show instance for natural numbers. */ - public static final Show naturalShow = bigintShow.comap(new F() { - public BigInteger f(final Natural natural) { - return natural.bigIntegerValue(); - } - }); + public static final Show naturalShow = bigintShow.contramap(Natural::bigIntegerValue); /** * A show instance for streams that splits into lines. @@ -638,26 +701,18 @@ public BigInteger f(final Natural natural) { * @return A show instance for streams that splits into lines. */ public static Show> unlineShow(final Show sa) { - return new Show>(new F, Stream>() { - public Stream f(final Stream as) { - return join(as.map(sa.show_()).intersperse(fromString("\n"))); - } - }); + return show(as -> join(as.map(sa.show_()).intersperse(fromString("\n")))); } /** * A show instance for lazy strings. */ - public static final Show lazyStringShow = show(new F>() { - public Stream f(final LazyString string) { - return string.toStream(); - } - }); + public static final Show lazyStringShow = show(LazyString::toStream); /** * 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. @@ -667,10 +722,23 @@ public Stream f(final LazyString string) { * @return a show instance for heterogeneous Streams. */ public static > Show> HListShow(final Show e, final Show l) { - return show(new F, Stream>() { - public Stream f(final HList.HCons c) { - return e.show(c.head()).cons('[').append(l.show(c.tail())).snoc(']'); - } - }); + 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 934201e8..8418a621 100644 --- a/core/src/main/java/fj/Try.java +++ b/core/src/main/java/fj/Try.java @@ -10,66 +10,70 @@ 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 TryCatch0 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try0 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch0 to promote + * @param t A Try0 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public P1> f(final Try0 t) { - return P.lazy(u -> { + @SuppressWarnings("unchecked") + public static P1> f(final Try0 t) { + return P.lazy(() -> { try { - return Validation.success(t.f()); + return success(t.f()); } catch (Exception e) { - return Validation.fail((E) e); + return fail((E) e); } }); } /** - * Promotes the TryCatch1 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try1 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch1 to promote + * @param t A Try1 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F> f(final Try1 t) { + @SuppressWarnings("unchecked") + 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 Validation.fail((E) e); + return fail((E) e); } }; } /** - * Promotes the TryCatch2 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try2 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch2 to promote + * @param t A Try2 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F2> f(final Try2 t) { + @SuppressWarnings("unchecked") + public static F2> f(final Try2 t) { return (a, b) -> { try { - return Validation.success(t.f(a, b)); + return success(t.f(a, b)); } catch (Exception e) { - return Validation.fail((E) e); + return fail((E) e); } }; } /** - * Promotes the TryCatch3 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try3 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch3 to promote + * @param t A Try3 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F3> f(final Try3 t) { + @SuppressWarnings("unchecked") + public static F3> f(final Try3 t) { return (a, b, c) -> { try { return success(t.f(a, b, c)); @@ -80,87 +84,92 @@ static public F3> f( } /** - * Promotes the TryCatch4 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try4 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch4 to promote + * @param t A Try4 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F4> f(final Try4 t) { + @SuppressWarnings("unchecked") + public static F4> f(final Try4 t) { return (a, b, c, d) -> { try { - return Validation.success(t.f(a, b, c, d)); + return success(t.f(a, b, c, d)); } catch (Exception ex) { - return Validation.fail((Z) ex); + return fail((Z) ex); } }; } /** - * Promotes the TryCatch5 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try5 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch5 to promote + * @param t A Try5 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F5> f(final Try5 t) { + @SuppressWarnings("unchecked") + public static F5> f(final Try5 t) { return (a, b, c, d, e) -> { try { - return Validation.success(t.f(a, b, c, d, e)); + return success(t.f(a, b, c, d, e)); } catch (Exception ex) { - return Validation.fail((Z) ex); + return fail((Z) ex); } }; } /** - * Promotes the TryCatch6 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try6 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch6 to promote + * @param t A Try6 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F6> f(final Try6 t) { + @SuppressWarnings("unchecked") + public static F6> f(final Try6 t) { return (a, b, c, d, e, f) -> { try { - return Validation.success(t.f(a, b, c, d, e, f)); + return success(t.f(a, b, c, d, e, f)); } catch (Exception ex) { - return Validation.fail((Z) ex); + return fail((Z) ex); } }; } /** - * Promotes the TryCatch7 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try7 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch7 to promote + * @param t A Try7 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F7> f(final Try7 t) { + @SuppressWarnings("unchecked") + public static F7> f(final Try7 t) { return (a, b, c, d, e, f, g) -> { try { - return Validation.success(t.f(a, b, c, d, e, f, g)); + return success(t.f(a, b, c, d, e, f, g)); } catch (Exception ex) { - return Validation.fail((Z) ex); + return fail((Z) ex); } }; } /** - * Promotes the TryCatch8 to a Validation that returns an Exception on the failure side and its result on the success side. + * Promotes the Try8 to a Validation that returns an Exception on the failure side and its result on the success side. * - * @param t A TryCatch8 to promote + * @param t A Try8 to promote * @return A Validation with an Exception on the failure side and its result on the success side. */ - static public F8> f(final Try8 t) { + @SuppressWarnings("unchecked") + public static F8> f(final Try8 t) { return (a, b, c, d, e, f, g, h) -> { try { - return Validation.success(t.f(a, b, c, d, e, f, g, h)); + return success(t.f(a, b, c, d, e, f, g, h)); } catch (Exception ex) { - return Validation.fail((Z) ex); + return fail((Z) ex); } }; } public static IO io(Try0 t) { - return IOFunctions.io(t); + return IOFunctions.fromTry(t); } } diff --git a/core/src/main/java/fj/TryEffect.java b/core/src/main/java/fj/TryEffect.java index 7db7ee11..a240e638 100644 --- a/core/src/main/java/fj/TryEffect.java +++ b/core/src/main/java/fj/TryEffect.java @@ -1,21 +1,15 @@ 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(){} + @SuppressWarnings("unchecked") public static P1> f(TryEffect0 t) { - return P.lazy(u -> { + return P.lazy(() -> { try { t.f(); return Validation.success(Unit.unit()); @@ -25,6 +19,7 @@ public static P1> f(TryEffect0 t }); } + @SuppressWarnings("unchecked") public static F> f(TryEffect1 t) { return a -> { try { @@ -37,6 +32,7 @@ public static F> f(TryEffect1 F2> f(TryEffect2 t) { return (a, b) -> { try { @@ -49,6 +45,7 @@ public static F2> f(TryEff } + @SuppressWarnings("unchecked") public static F3> f(TryEffect3 t) { return (a, b, c) -> { try { @@ -60,6 +57,7 @@ public static F3> f( }; } + @SuppressWarnings("unchecked") public static F4> f(TryEffect4 t) { return (a, b, c, d) -> { try { @@ -71,6 +69,7 @@ public static F4 F5> f(TryEffect5 t) { return (a, b, c, d, e) -> { try { @@ -82,6 +81,7 @@ public static F5 F6> f(TryEffect6 t) { return (a, b, c, d, e, f) -> { try { @@ -93,6 +93,7 @@ public static F5 F7> f(TryEffect7 t) { return (a, b, c, d, e, f, g) -> { try { @@ -104,6 +105,7 @@ public static F5 F8> f(TryEffect8 t) { return (a, b, c, d, e, f, g, h) -> { try { diff --git a/core/src/main/java/fj/Unit.java b/core/src/main/java/fj/Unit.java index 60ef3750..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(); @@ -20,4 +18,10 @@ private Unit() { public static Unit unit() { return u; } + + @Override + public String toString() { + return "unit"; + } + } diff --git a/core/src/main/java/fj/Void.java b/core/src/main/java/fj/Void.java new file mode 100644 index 00000000..6a9aaf07 --- /dev/null +++ b/core/src/main/java/fj/Void.java @@ -0,0 +1,16 @@ +package fj; + +/** + * A logically uninhabited data type. + */ +public abstract class Void { + + private Void() { + throw new IllegalStateException("Void cannot be instantiated"); + } + + /** + * Since Void values logically don't exist, this witnesses the logical reasoning tool of "ex falso quodlibet". + */ + public abstract X absurd(); +} diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 70f29f9b..5c787f04 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -1,28 +1,24 @@ package fj.control; -import fj.Bottom; -import fj.F; -import fj.F1Functions; -import fj.F2; -import fj.P1; +import fj.*; import fj.data.Either; -import fj.F2Functions; + import static fj.Function.curry; import static fj.data.Either.left; import static fj.data.Either.right; /** * 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); } } @@ -47,63 +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, new F>() { - public Trampoline f(final Object o) { - return suspend(new P1>() { - public Trampoline _1() { - return 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(new F>, P1>>() { - public P1> f(final P1> p) { - return p.map(new F, Trampoline>() { - public Trampoline f(final Trampoline ot) { - return ot.fold(new F, Trampoline>() { - public Trampoline f(final Normal o) { - return o.foldNormal(new F>() { - public Trampoline f(final Object o) { - return cont.f(o); - } - }, new F>, Trampoline>() { - public Trampoline f(final P1> t) { - return t._1().bind(cont); - } - } - ); - } - }, new F, Trampoline>() { - public Trampoline f(final Codense c) { - return codense(c.sub, new F>() { - public Trampoline f(final Object o) { - return c.cont.f(o).bind(cont); - } - }); - } - } - ); - } - }); - } - }, new F>>() { - public P1> f(final Object o) { - return new P1>() { - public Trampoline _1() { - return 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)))); } } @@ -151,19 +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 new F>() { - public Trampoline f(final A a) { - return pure(a); - } - }; + return Trampoline::pure; } /** @@ -173,7 +123,17 @@ public Trampoline f(final A a) { * @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)); } /** @@ -183,18 +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 new F>, Trampoline>() { - public Trampoline f(final P1> trampolineP1) { - return suspend(trampolineP1); - } - }; + return Trampoline::suspend; } protected abstract R fold(final F, R> n, final F, R> gs); @@ -214,48 +171,28 @@ public Trampoline f(final P1> trampolineP1) { * @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 new F>, F, Trampoline>>() { - public F, Trampoline> f(final F> f) { - return new F, Trampoline>() { - public Trampoline f(final Trampoline a) { - return a.bind(f); - } - }; - } - }; + 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 new F, F, Trampoline>>() { - public F, Trampoline> f(final F f) { - return new F, Trampoline>() { - public Trampoline f(final Trampoline a) { - return a.map(f); - } - }; - } - }; + 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 new F, Either>, A>>() { - public Either>, A> f(final Trampoline aTrampoline) { - return aTrampoline.resume(); - } - }; + return Trampoline::resume; } /** @@ -271,7 +208,7 @@ public Either>, A> f(final Trampoline aTrampoline) { * @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(); @@ -291,11 +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(new F, Trampoline>() { - public Trampoline f(final F f) { - return map(f); - } - }); + return lf.bind(this::map); } /** @@ -317,11 +250,7 @@ public final Trampoline bind(final Trampoline lb, final F F, F, Trampoline>> liftM2(final F> f) { - return curry(new F2, Trampoline, Trampoline>() { - public Trampoline f(final Trampoline as, final Trampoline bs) { - return as.bind(bs, f); - } - }); + return curry((as, bs) -> as.bind(bs, f)); } /** @@ -332,39 +261,24 @@ public Trampoline f(final Trampoline as, final Trampoline bs) { * @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(new F2, Trampoline, Trampoline>() { - public Trampoline f(final Trampoline ta, final Trampoline tb) { - return suspend(new P1>() { - public Trampoline _1() { - return 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(new F, Trampoline>() { - public Trampoline f(final Trampoline ta) { - return 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(new P1>() { - public Trampoline _1() { - return 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 4c891254..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,21 +36,28 @@ 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. * * @return The callable-valued function which is isomorphic to this database action. */ public final F> asFunction() { - return new F>() { - public Callable f(final Connection c) { - return new Callable() { - public A call() throws Exception { - return run(c); - } - }; - } - }; + return c -> () -> run(c); } /** @@ -73,11 +81,7 @@ public B run(final Connection c) throws SQLException { * @return A function equivalent to the given one, which operates on values in the database. */ public static F, DB> liftM(final F f) { - return new F, DB>() { - public DB f(final DB a) { - return a.map(f); - } - }; + return a -> a.map(f); } /** @@ -115,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 86201147..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,21 +69,17 @@ 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; - f = new F>() { - public P1 f(final A a) { - return s.par(e.f(a)); - } - }; + f = a -> s.par(e.f(a)); } /** @@ -94,7 +90,7 @@ public P1 f(final A a) { * @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))); } /** @@ -105,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); } /** @@ -126,12 +122,8 @@ public P1 act(final A a) { * @param f The function to use for the transformation * @return A new actor which passes its messages through the given function, to this actor. */ - public Actor comap(final F f) { - return actor(s, new F>() { - public P1 f(final B b) { - return act(f.f(b)); - } - }); + public Actor contramap(final F f) { + return actor(s, (B b) -> act(f.f(b))); } /** @@ -140,11 +132,7 @@ public P1 f(final B b) { * @return A new actor, equivalent to this actor, that acts on promises. */ public Actor> promise() { - return actor(s, new Effect1>() { - public void f(final Promise b) { - b.to(Actor.this); - } - }); + return actor(s, (Promise b) -> b.to(Actor.this)); } } diff --git a/core/src/main/java/fj/control/parallel/Callables.java b/core/src/main/java/fj/control/parallel/Callables.java index b011a0a6..995780c4 100644 --- a/core/src/main/java/fj/control/parallel/Callables.java +++ b/core/src/main/java/fj/control/parallel/Callables.java @@ -1,10 +1,9 @@ package fj.control.parallel; -import fj.F; -import fj.F2; -import fj.Function; +import fj.*; + import static fj.Function.curry; -import fj.P1; + import fj.data.Either; import static fj.data.Either.left; import static fj.data.Either.right; @@ -31,11 +30,7 @@ private Callables() { * @return A Callable that yields the argument when called. */ public static Callable callable(final A a) { - return new Callable() { - public A call() throws Exception { - return a; - } - }; + return () -> a; } /** @@ -45,10 +40,8 @@ public A call() throws Exception { * @return A callable that always throws the given exception. */ public static Callable callable(final Exception e) { - return new Callable() { - public A call() throws Exception { - throw e; - } + return () -> { + throw e; }; } @@ -58,11 +51,7 @@ public A call() throws Exception { * @return A function from a value to a Callable that completely preserves that value. */ public static F> callable() { - return new F>() { - public Callable f(final A a) { - return callable(a); - } - }; + return Callables::callable; } /** @@ -73,15 +62,7 @@ public Callable f(final A a) { * @return The equivalent function whose return value is wrapped in a Callable. */ public static F> callable(final F f) { - return new F>() { - public Callable f(final A a) { - return new Callable() { - public B call() { - return f.f(a); - } - }; - } - }; + return a -> () -> f.f(a); } /** @@ -91,11 +72,7 @@ public B call() { * @return A transformation from a function to the equivalent Callable-valued function. */ public static F, F>> arrow() { - return new F, F>>() { - public F> f(final F f) { - return callable(f); - } - }; + return Callables::callable; } /** @@ -106,11 +83,7 @@ public F> f(final F f) { * @return The result of applying the function in the second argument to the value of the Callable in the first. */ public static Callable bind(final Callable a, final F> f) { - return new Callable() { - public B call() throws Exception { - return f.f(a.call()).call(); - } - }; + return () -> f.f(a.call()).call(); } /** @@ -120,11 +93,7 @@ public B call() throws Exception { * @return That function lifted to a function on Callables. */ public static F, Callable> fmap(final F f) { - return new F, Callable>() { - public Callable f(final Callable a) { - return Callables.bind(a, callable(f)); - } - }; + return a -> bind(a, callable(f)); } /** @@ -135,11 +104,7 @@ public Callable f(final Callable a) { * @return A new callable after applying the given callable function to the first argument. */ public static Callable apply(final Callable ca, final Callable> cf) { - return bind(cf, new F, Callable>() { - public Callable f(final F f) { - return fmap(f).f(ca); - } - }); + return bind(cf, f -> fmap(f).f(ca)); } /** @@ -161,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()); } /** @@ -171,11 +136,7 @@ public static Callable join(final Callable> a) { * @return A function of arity-2 promoted to map over callables. */ public static F, F, Callable>> liftM2(final F> f) { - return curry(new F2, Callable, Callable>() { - public Callable f(final Callable ca, final Callable cb) { - return bind(ca, cb, f); - } - }); + return curry((ca, cb) -> bind(ca, cb, f)); } /** @@ -185,8 +146,7 @@ public Callable f(final Callable ca, final Callable cb) { * @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())); } /** @@ -195,11 +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 new F>, Callable>>() { - public Callable> f(final List> as) { - return sequence(as); - } - }; + return Callables::sequence; } /** @@ -209,16 +165,13 @@ public Callable> f(final List> as) { * @return An optional value that yields the value in the Callable, or None if the Callable fails. */ public static P1> option(final Callable a) { - return new P1>() { - @SuppressWarnings({"UnusedCatchParameter"}) - public Option _1() { + return P.lazy(() -> { try { return some(a.call()); } catch (Exception e) { return none(); } - } - }; + }); } /** @@ -227,11 +180,7 @@ public Option _1() { * @return a function that turns a Callable into an optional value. */ public static F, P1>> option() { - return new F, P1>>() { - public P1> f(final Callable a) { - return option(a); - } - }; + return Callables::option; } /** @@ -241,15 +190,13 @@ public P1> f(final Callable a) { * @return Either the value in the given Callable, or the Exception with which the Callable fails. */ public static P1> either(final Callable a) { - return new P1>() { - public Either _1() { + return P.lazy(() -> { try { - return right(a.call()); + return right(a.call()); } catch (Exception e) { - return left(e); + return left(e); } - } - }; + }); } /** @@ -258,11 +205,7 @@ public Either _1() { * @return a function that turns a Callable into an Either. */ public static F, P1>> either() { - return new F, P1>>() { - public P1> f(final Callable a) { - return either(a); - } - }; + return Callables::either; } /** @@ -271,15 +214,13 @@ public P1> f(final Callable a) { * @param e Either an exception or a value to wrap in a Callable * @return A Callable equivalent to the given Either value. */ - public static Callable fromEither(final P1> e) { - return new Callable() { - public A call() throws Exception { - final Either e1 = e._1(); - if (e1.isLeft()) - throw e1.left().value(); - else - return e1.right().value(); - } + public static Callable fromEither(final F0> e) { + return () -> { + final Either e1 = e.f(); + if (e1.isLeft()) + throw e1.left().value(); + else + return e1.right().value(); }; } @@ -289,11 +230,7 @@ public A call() throws Exception { * @return a function that turns an Either into a Callable. */ public static F>, Callable> fromEither() { - return new F>, Callable>() { - public Callable f(final P1> e) { - return fromEither(e); - } - }; + return Callables::fromEither; } /** @@ -302,15 +239,13 @@ public Callable f(final P1> e) { * @param o An optional value to turn into a Callable. * @return A Callable that yields some value or throws an exception in the case of no value. */ - public static Callable fromOption(final P1> o) { - return new Callable() { - public A call() throws Exception { - final Option o1 = o._1(); - if (o1.isSome()) - return o1.some(); - else - throw new Exception("No value."); - } + public static Callable fromOption(final F0> o) { + return () -> { + final Option o1 = o.f(); + if (o1.isSome()) + return o1.some(); + else + throw new Exception("No value."); }; } @@ -321,11 +256,7 @@ public A call() throws Exception { * or throws an exception in the case of no value. */ public static F>, Callable> fromOption() { - return new F>, Callable>() { - public Callable f(final P1> o) { - return fromOption(o); - } - }; + return Callables::fromOption; } /** @@ -349,11 +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 new F, Callable>() { - public Callable f(final Callable a) { - return 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 9d04fc7a..b9eeacbf 100644 --- a/core/src/main/java/fj/control/parallel/ParModule.java +++ b/core/src/main/java/fj/control/parallel/ParModule.java @@ -56,11 +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 new F, Promise>() { - public Promise f(final P1 ap1) { - return promise(ap1); - } - }; + return this::promise; } /** @@ -71,7 +67,7 @@ public Promise f(final P1 ap1) { * that can be claimed in the future. */ public F> promise(final F f) { - return F1Functions.promiseK(f, strategy); + return f.promiseK(strategy); } /** @@ -81,11 +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 new F, F>>() { - public F> f(final F abf) { - return promise(abf); - } - }; + return this::promise; } /** @@ -96,7 +88,7 @@ public F> f(final F abf) { * 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)); } @@ -118,11 +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 new F, Actor>() { - public Actor f(final Effect1 effect) { - return effect(effect); - } - }; + return this::effect; } /** @@ -141,11 +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 new F, Actor>() { - public Actor f(final Effect1 effect) { - return actor(effect); - } - }; + return this::actor; } /** @@ -164,11 +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 new F>, Promise>>() { - public Promise> f(final List> list) { - return sequence(list); - } - }; + return this::sequence; } /** @@ -187,11 +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 new F>, Promise>>() { - public Promise> f(final Stream> stream) { - return sequence(stream); - } - }; + return this::sequence; } /** @@ -222,11 +198,7 @@ public Promise> mapM(final List as, final F> f) * @return a function that maps a concurrent function over a List inside a promise. */ public F>, F, Promise>>> mapList() { - return curry(new F2>, List, Promise>>() { - public Promise> f(final F> f, final List list) { - return mapM(list, f); - } - }); + return curry((f, list) -> mapM(list, f)); } /** @@ -247,11 +219,7 @@ public Promise> mapM(final Stream as, final F> * @return a function that maps a concurrent function over a Stream inside a promise. */ public F>, F, Promise>>> mapStream() { - return curry(new F2>, Stream, Promise>>() { - public Promise> f(final F> f, final Stream stream) { - return mapM(stream, f); - } - }); + return curry((f, stream) -> mapM(stream, f)); } /** @@ -282,11 +250,7 @@ public Promise> parMap(final List as, final F f) { * @return A function that maps another function across a list in parallel. */ public F, F, Promise>>> parMapList() { - return curry(new F2, List, Promise>>() { - public Promise> f(final F abf, final List list) { - return parMap(list, abf); - } - }); + return curry((abf, list) -> parMap(list, abf)); } /** @@ -297,11 +261,7 @@ public Promise> f(final F abf, final List list) { * @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(new F, NonEmptyList>() { - public NonEmptyList f(final List list) { - return NonEmptyList.fromList(list).some(); - } - }); + return mapM(as.toList(), promise(f)).fmap(list -> NonEmptyList.fromList(list).some()); } /** @@ -321,11 +281,7 @@ public Promise> parMap(final Stream as, final F f) { * @return A function that maps another function across a stream in parallel. */ public F, F, Promise>>> parMapStream() { - return curry(new F2, Stream, Promise>>() { - public Promise> f(final F abf, final Stream stream) { - return parMap(stream, abf); - } - }); + return curry((abf, stream) -> parMap(stream, abf)); } /** @@ -337,7 +293,7 @@ public Promise> f(final F abf, final Stream stream) { */ 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())); } /** @@ -346,11 +302,7 @@ public Promise> parMap(final Iterable as, final F f) * @return A function that maps another function across an iterable in parallel. */ public F, F, Promise>>> parMapIterable() { - return curry(new F2, Iterable, Promise>>() { - public Promise> f(final F abf, final Iterable iterable) { - return parMap(iterable, abf); - } - }); + return curry((abf, iterable) -> parMap(iterable, abf)); } /** @@ -361,11 +313,7 @@ public Promise> f(final F abf, final Iterable iterable) { * @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(new F, Array>() { - public Array f(final Stream stream) { - return stream.toArray(); - } - }); + return parMap(as.toStream(), f).fmap(Stream::toArray); } /** @@ -374,11 +322,7 @@ public Array f(final Stream stream) { * @return A function that maps another function across an array in parallel. */ public F, F, Promise>>> parMapArray() { - return curry(new F2, Array, Promise>>() { - public Promise> f(final F abf, final Array array) { - return parMap(array, abf); - } - }); + return curry((abf, array) -> parMap(array, abf)); } /** @@ -390,7 +334,7 @@ public Promise> f(final F abf, final Array array) { */ 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())))); } /** @@ -402,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())); } /** @@ -411,11 +355,7 @@ public Promise> parMap(final Tree ta, final F f) { * @return A function that maps a given function across a Tree in parallel. */ public F, F, Promise>>> parMapTree() { - return curry(new F2, Tree, Promise>>() { - public Promise> f(final F abf, final Tree tree) { - return parMap(tree, abf); - } - }); + return curry((abf, tree) -> parMap(tree, abf)); } /** @@ -429,14 +369,9 @@ public Promise> parMap(final TreeZipper za, final F, Tree> tf = Tree.fmap_().f(f); final P4, Stream>, Stream>, Stream>, A, Stream>>>> p = za.p(); return mapM(p._4(), - new F>, A, Stream>>, Promise>, B, Stream>>>>() { - public Promise>, B, Stream>>> f( - final P3>, A, Stream>> p3) { - return 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())))); + p3 -> parMap(p3._3(), tf).apply(promise(f).f(p3._2()).apply( + 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())))); } /** @@ -447,7 +382,7 @@ public Promise>, B, Stream>>> f( * @return A promise of a new List with the given function bound across its elements. */ public Promise> parFlatMap(final List as, final F> f) { - return parFoldMap(as, f, Monoid.listMonoid()); + return parFoldMap(as, f, Monoid.listMonoid()); } /** @@ -458,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()); } /** @@ -469,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()); } /** @@ -480,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())); } /** @@ -493,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)))); } /** @@ -505,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)))); } /** @@ -517,11 +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(new F, Array>() { - public Array f(final Stream stream) { - return stream.toArray(); - } - }); + return parZipWith(as.toStream(), bs.toStream(), f).fmap(Stream::toArray); } /** @@ -534,7 +465,7 @@ public Array f(final Stream stream) { */ 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())); } /** @@ -565,15 +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(new F, Option, Stream>>>() { - public Option, Stream>> f(final Stream stream) { - return stream.isEmpty() ? Option., Stream>>none() : some(chunking.f(stream)); - } - }, as), Stream.map_().f(map)).bind(new F>, Promise>() { - public Promise f(final Stream> stream) { - return 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())); } /** @@ -591,15 +514,9 @@ public Promise f(final Stream> stream) { */ public Promise parFoldMap(final Iterable as, final F map, final Monoid reduce, final F, P2, Iterable>> chunking) { - return parFoldMap(iterableStream(as), map, reduce, new F, P2, Stream>>() { - public P2, Stream> f(final Stream stream) { - final F, Stream> is = new F, Stream>() { - public Stream f(final Iterable iterable) { - return iterableStream(iterable); - } - }; - return chunking.f(stream).map1(is).map2(is); - } + return parFoldMap(iterableStream(as), map, reduce, (Stream stream) -> { + 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 81389e26..3689c286 100644 --- a/core/src/main/java/fj/control/parallel/Promise.java +++ b/core/src/main/java/fj/control/parallel/Promise.java @@ -1,12 +1,7 @@ package fj.control.parallel; -import fj.Effect; -import fj.F; -import fj.F2; -import fj.P; -import fj.P1; -import fj.P2; -import fj.Unit; +import fj.*; + import static fj.P.p; import static fj.Function.curry; import static fj.Function.identity; @@ -42,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; @@ -67,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); } /** @@ -80,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; } @@ -91,11 +86,7 @@ public static Promise promise(final Strategy s, final P1 a) { * @return A function that, given a 1-product, yields a promise of that product's value. */ public static F, Promise> promise(final Strategy s) { - return new F, Promise>() { - public Promise f(final P1 a) { - return promise(s, a); - } - }; + return a -> promise(s, a); } /** @@ -106,11 +97,7 @@ public Promise f(final P1 a) { * @return A promise of a new Callable that will return the result of calling the given Callable. */ public static Promise> promise(final Strategy s, final Callable a) { - return promise(s, new P1>() { - public Callable _1() { - return normalise(a); - } - }); + return promise(s, P.lazy(() -> normalise(a))); } /** @@ -122,11 +109,7 @@ public Callable _1() { * @return The given function transformed into a function that returns a promise. */ public static F> promise(final Strategy s, final F f) { - return new F>() { - public Promise f(final A a) { - return promise(s, P1.curry(f).f(a)); - } - }; + return a -> promise(s, P1.curry(f).f(a)); } /** @@ -135,7 +118,7 @@ public Promise f(final A a) { * @param a An actor that will receive this Promise's value in the future. */ public void to(final Actor a) { - actor.act(P.p(Either., Actor>right(a), this)); + actor.act(p(Either.right(a), this)); } /** @@ -155,11 +138,7 @@ public Promise fmap(final F f) { * @return That function lifted to a function on Promises. */ public static F, Promise> fmap_(final F f) { - return new F, Promise>() { - public Promise f(final Promise a) { - return a.fmap(f); - } - }; + return a -> a.fmap(f); } /** @@ -197,10 +176,10 @@ 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().comap(f)); + to(ab.promise().contramap(f)); return r; } @@ -211,11 +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(new F, Promise>() { - public Promise f(final F f) { - return fmap(f); - } - }); + return pf.bind(this::fmap); } /** @@ -247,11 +222,7 @@ public Promise bind(final P1> p, final F> f) { * @return A function of arity-2 promoted to map over promises. */ public static F, F, Promise>> liftM2(final F> f) { - return curry(new F2, Promise, Promise>() { - public Promise f(final Promise ca, final Promise cb) { - return ca.bind(cb, f); - } - }); + return curry((ca, cb) -> ca.bind(cb, f)); } /** @@ -262,7 +233,7 @@ public Promise f(final Promise ca, final Promise cb) { * @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)); } /** @@ -272,11 +243,7 @@ public static Promise> sequence(final Strategy s, final List

F>, Promise>> sequence(final Strategy s) { - return new F>, Promise>>() { - public Promise> f(final List> as) { - return sequence(s, as); - } - }; + return as -> sequence(s, as); } /** @@ -287,15 +254,7 @@ public Promise> f(final List> as) { * @return A single promise for the given Stream. */ public static Promise> sequence(final Strategy s, final Stream> as) { - return join(foldRightS(s, curry(new F2, P1>>, Promise>>() { - public Promise> f(final Promise o, final P1>> p) { - return o.bind(new F>>() { - public Promise> f(final A a) { - return 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)); } /** @@ -305,11 +264,7 @@ public Promise> f(final A a) { * @return A function that turns a list of promises into a single promise of a Stream.. */ public static F>, Promise>> sequenceS(final Strategy s) { - return new F>, Promise>>() { - public Promise> f(final List> as) { - return sequence(s, as); - } - }; + return as -> sequence(s, as); } /** @@ -320,7 +275,7 @@ public Promise> f(final List> as) { * @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()); } /** @@ -334,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()))); } }; @@ -352,12 +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, new P1>>() { - public Promise> _1() { - return 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())))); } }; } @@ -409,11 +360,7 @@ public boolean isFulfilled() { * @return A new promise of the result of applying the given function to this promise. */ public Promise cobind(final F, B> f) { - return promise(s, new P1() { - public B _1() { - return f.f(Promise.this); - } - }); + return promise(s, P.lazy(() -> f.f(Promise.this))); } /** @@ -434,12 +381,8 @@ public Promise> cojoin() { */ public Stream sequenceW(final Stream, B>> fs) { return fs.isEmpty() - ? Stream.nil() - : Stream.cons(fs.head().f(this), new P1>() { - public Stream _1() { - return sequenceW(fs.tail()._1()); - } - }); + ? 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 daf8098e..2b77a9e8 100644 --- a/core/src/main/java/fj/control/parallel/Strategy.java +++ b/core/src/main/java/fj/control/parallel/Strategy.java @@ -1,6 +1,5 @@ package fj.control.parallel; -import fj.Effect; import fj.F; import fj.F2; import fj.Function; @@ -52,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); } /** @@ -72,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)); } /** @@ -82,11 +81,7 @@ public F> concurry(final F f) { * @return A function that executes concurrently when called, yielding a product-1 that returns the value. */ public F>> concurry(final F2 f) { - return new F>>() { - public F> f(final B b) { - return concurry(curry(f).f(b)); - } - }; + return b -> concurry(curry(f).f(b)); } /** @@ -96,7 +91,7 @@ public F> f(final B b) { * @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()); } /** @@ -141,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); } /** @@ -154,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); } /** @@ -164,11 +159,7 @@ public Array parMap1(final F f, final Array bs) { * @return The function transformed into a parallel function on lists. */ public F, P1>> parMapList(final F f) { - return new F, P1>>() { - public P1> f(final List as) { - return parMap(f, as); - } - }; + return as -> parMap(f, as); } /** @@ -177,11 +168,7 @@ public P1> f(final List as) { * @return A function that promotes another function to a parallel function on lists. */ public F, F, P1>>> parMapList() { - return new F, F, P1>>>() { - public F, P1>> f(final F f) { - return parMapList(f); - } - }; + return this::parMapList; } /** @@ -190,15 +177,7 @@ public F, P1>> f(final F f) { * @return A function that promotes another function to a blocking parallel function on lists. */ public F, F, List>> parMapList1() { - return new F, F, List>>() { - public F, List> f(final F f) { - return new F, List>() { - public List f(final List bs) { - return parMap1(f, bs); - } - }; - } - }; + return f1 -> bs -> parMap1(f1, bs); } /** @@ -208,11 +187,7 @@ public List f(final List bs) { * @return The function transformed into a parallel function on arrays. */ public F, P1>> parMapArray(final F f) { - return new F, P1>>() { - public P1> f(final Array as) { - return parMap(f, as); - } - }; + return as -> parMap(f, as); } /** @@ -221,11 +196,7 @@ public P1> f(final Array as) { * @return A function that promotes another function to a parallel function on arrays. */ public F, F, P1>>> parMapArray() { - return new F, F, P1>>>() { - public F, P1>> f(final F f) { - return parMapArray(f); - } - }; + return this::parMapArray; } /** @@ -234,15 +205,7 @@ public F, P1>> f(final F f) { * @return A function that promotes another function to a blocking parallel function on arrays. */ public F, F, Array>> parMapArray1() { - return new F, F, Array>>() { - public F, Array> f(final F f) { - return new F, Array>() { - public Array f(final Array bs) { - return parMap1(f, bs); - } - }; - } - }; + return f1 -> bs -> parMap1(f1, bs); } /** @@ -256,7 +219,7 @@ public Array f(final Array bs) { public static P1> parFlatMap(final Strategy> s, final F> f, final List as) { - return P1.fmap(List.join()).f(s.parMap(f, as)); + return P1.map_(List.join()).f(s.parMap(f, as)); } /** @@ -270,7 +233,7 @@ public static P1> parFlatMap(final Strategy> s, public static P1> parFlatMap(final Strategy> s, final F> f, final Array as) { - return P1.fmap(Array.join()).f(s.parMap(f, as)); + return P1.map_(Array.join()).f(s.parMap(f, as)); } /** @@ -285,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.fmap(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()))); } /** @@ -328,11 +291,7 @@ public P1> parZipWith(final F2 f, final Array bs, fi * @return A transformation that zips two lists using the argument function, in parallel. */ public F2, List, P1>> parZipListWith(final F2 f) { - return new F2, List, P1>>() { - public P1> f(final List bs, final List cs) { - return parZipWith(f, bs, cs); - } - }; + return (bs, cs) -> parZipWith(f, bs, cs); } /** @@ -343,11 +302,7 @@ public P1> f(final List bs, final List cs) { * @return A transformation that zips two arrays using the argument function, in parallel. */ public F2, Array, P1>> parZipArrayWith(final F2 f) { - return new F2, Array, P1>>() { - public P1> f(final Array bs, final Array cs) { - return parZipWith(f, bs, cs); - } - }; + return (bs, cs) -> parZipWith(f, bs, cs); } /** @@ -356,11 +311,7 @@ public P1> f(final Array bs, final Array cs) { * @return A function which, given a Future, yields a product-1 that waits for it. */ public static F, P1> obtain() { - return new F, P1>() { - public P1 f(final Future t) { - return obtain(t); - } - }; + return Strategy::obtain; } /** @@ -370,8 +321,7 @@ public P1 f(final Future t) { * @return A product-1 that waits for the given future to obtain a value. */ public static P1 obtain(final Future t) { - return new P1() { - public A _1() { + return P.lazy(() -> { try { return t.get(); } catch (InterruptedException e) { @@ -380,8 +330,7 @@ public A _1() { } catch (ExecutionException e) { throw new Error(e); } - } - }; + }); } /** @@ -390,11 +339,7 @@ public A _1() { * @return An effect, which, given a Future, waits for it to obtain a value, discarding the value. */ public static Effect1> discard() { - return new Effect1>() { - public void f(final Future a) { - Strategy.obtain().f(a)._1(); - } - }; + return a -> Strategy.obtain().f(a)._1(); } /** @@ -405,12 +350,10 @@ public void f(final Future a) { * every evaluation. */ public static Strategy simpleThreadStrategy() { - return strategy(new F, P1>() { - public P1 f(final P1 p) { - final FutureTask t = new FutureTask(Java.P1_Callable().f(p)); - new Thread(t).start(); - return obtain(t); - } + return strategy(p -> { + final FutureTask t = new FutureTask<>(Java.P1_Callable().f(p)); + new Thread(t).start(); + return obtain(t); }); } @@ -423,11 +366,7 @@ public P1 f(final P1 p) { * of parallelism. */ public static Strategy executorStrategy(final ExecutorService s) { - return strategy(new F, P1>() { - public P1 f(final P1 p) { - return obtain(s.submit(Java.P1_Callable().f(p))); - } - }); + return strategy(p -> obtain(s.submit(Java.P1_Callable().f(p)))); } /** @@ -439,11 +378,7 @@ public P1 f(final P1 p) { * and notifies the service of task completion. */ public static Strategy completionStrategy(final CompletionService s) { - return strategy(new F, P1>() { - public P1 f(final P1 p) { - return obtain(s.submit(Java.P1_Callable().f(p))); - } - }); + return strategy(p -> obtain(s.submit(Java.P1_Callable().f(p)))); } /** @@ -452,11 +387,7 @@ public P1 f(final P1 p) { * @return A strategy that performs sequential (non-concurrent) evaluation of its argument. */ public static Strategy seqStrategy() { - return strategy(new F, P1>() { - public P1 f(final P1 a) { - return P.p(a._1()); - } - }); + return strategy(a -> P.p(a._1())); } /** @@ -465,7 +396,7 @@ public P1 f(final P1 a) { * @return A strategy that performs no evaluation of its argument. */ public static Strategy idStrategy() { - return strategy(Function.>identity()); + return strategy(Function.identity()); } /** @@ -486,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()); } /** @@ -495,8 +426,8 @@ public Strategy map(final F, P1> f) { * @param f A transformation from the resulting strategy's domain to this strategy's domain. * @return A new strategy that applies the given transformation before each application of this strategy. */ - public Strategy comap(final F, P1> f) { - return xmap(Function.>identity(), f); + public Strategy contramap(final F, P1> f) { + return xmap(Function.identity(), f); } /** @@ -519,21 +450,16 @@ public Strategy errorStrategy(final Effect1 e) { * @return A strategy that captures any runtime errors with a side-effect. */ public static Strategy errorStrategy(final Strategy s, final Effect1 e) { - return s.comap(new F, P1>() { - public P1 f(final P1 a) { - return new P1() { - public A _1() { - try { - return a._1(); - } catch (Throwable t) { - final Error error = new Error(t); - e.f(error); - throw error; - } - } - }; - } - }); + return s.contramap(a -> P.lazy(() -> { + try { + return a._1(); + } catch (Throwable t) { + final Error error = new Error(t); + e.f(error); + throw error; + } + }) + ); } /** @@ -543,11 +469,7 @@ public A _1() { * @return A new strategy that fully evaluates Callables, using the given strategy. */ public static Strategy> callableStrategy(final Strategy> s) { - return s.comap(new F>, P1>>() { - public P1> f(final P1> a) { - return P1.curry(Callables.normalise()).f(a._1()); - } - }); + return s.contramap(a -> P1.curry(Callables.normalise()).f(a._1())); } } diff --git a/core/src/main/java/fj/data/$.java b/core/src/main/java/fj/data/$.java index 9418afd0..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; @@ -15,20 +15,16 @@ public final class $ extends P1 { this.b = b; } - /** - * @deprecated JDK 8 warns '_' may not be supported after SE 8. Replaced by {@link #constant} and synonym {@link #__} (prefer constant). - */ - @Deprecated - public static $ _(final B b) { - return constant(b); - } - + /** + * Returns a function that given an argument, returns a function that ignores its argument. + * @return A function that given an argument, returns a function that ignores its argument. + */ 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 43baa462..fb245f3f 100755 --- a/core/src/main/java/fj/data/Array.java +++ b/core/src/main/java/fj/data/Array.java @@ -1,30 +1,36 @@ package fj.data; -import fj.Effect; +import fj.Equal; import fj.F; +import fj.F0; import fj.F2; +import fj.Hash; import fj.P; -import fj.P1; import fj.P2; +import fj.Show; import fj.Unit; import fj.function.Effect1; -import static fj.Function.*; +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; + +import static fj.Function.constant; +import static fj.Function.curry; +import static fj.Function.identity; import static fj.P.p; import static fj.P.p2; import static fj.Unit.unit; import static fj.data.List.iterableList; import static fj.data.Option.none; import static fj.data.Option.some; - import static java.lang.Math.min; import static java.lang.System.arraycopy; -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; - /** * Provides an interface to arrays. * @@ -57,6 +63,11 @@ public A get(final int index) { return (A) a[index]; } + @Override + public int hashCode() { + return Hash.arrayHash(Hash.anyHash()).hash(this); + } + /** * Sets the element at the given index to the given value. * @@ -79,7 +90,7 @@ public int length() { } public ImmutableProjection immutable() { - return new ImmutableProjection(this); + return new ImmutableProjection<>(this); } /** @@ -127,7 +138,7 @@ public Object[] array() { */ @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]); } /** @@ -138,8 +149,8 @@ public Option toOption() { * @return An either projection of this array. */ @SuppressWarnings("unchecked") - public Either toEither(final P1 x) { - return a.length == 0 ? Either.left(x._1()) : Either.right((A) a[0]); + public Either toEither(final F0 x) { + return a.length == 0 ? Either.left(x.f()) : Either.right((A) a[0]); } /** @@ -165,12 +176,14 @@ public List toList() { */ @SuppressWarnings("unchecked") public Stream toStream() { - return Stream.unfold(new F>>() { - public Option> f(final Integer o) { - return a.length > o ? some(p((A) a[o], o + 1)) - : Option.>none(); - } - }, 0); + return Stream.unfold(o -> + a.length > o ? some(p((A) a[o], o + 1)) : Option.none(), 0 + ); + } + + @Override + public String toString() { + return Show.arrayShow(Show.anyShow()).showS(this); } /** @@ -179,7 +192,7 @@ public Option> f(final Integer o) { * @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]; @@ -187,7 +200,7 @@ public Array map(final F f) { bs[i] = f.f((A) a[i]); } - return new Array(bs); + return new Array<>(bs); } /** @@ -230,7 +243,7 @@ public Unit foreach(final F f) { * @param f The side-effect to perform for the given element. */ @SuppressWarnings("unchecked") - public void foreach(final Effect1 f) { + public void foreachDoEffect(final Effect1 f) { for (final Object x : a) { f.f((A) x); } @@ -300,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; @@ -310,7 +323,7 @@ public Array scanLeft(final F> f, final B b) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -334,18 +347,18 @@ 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); - bs[0] = x; + bs[0] = x; for (int i = 1; i < a.length; i++) { x = f.f(x).f((A) a[i]); bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -369,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; @@ -379,7 +392,7 @@ public Array scanRight(final F>f, final B b) { bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -403,18 +416,18 @@ 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); - bs[length() - 1] = x; + bs[length() - 1] = x; for (int i = a.length - 2; i >= 0; i--) { x = f.f((A) a[i]).f(x); bs[i] = x; } - return new Array(bs); + return new Array<>(bs); } /** @@ -436,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; @@ -459,7 +472,7 @@ public Unit f(final Array x) { } }); - return new Array(bs); + return new Array<>(bs); } /** @@ -504,15 +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(new F, Array>() { - public Array f(final F f) { - return map(new F() { - public B f(final A a) { - return f.f(a); - } - }); - } - }); + return lf.bind(f -> map(f)); } /** @@ -527,7 +532,7 @@ public Array reverse() { x[a.length - 1 - i] = a[i]; } - return new Array(x); + return new Array<>(x); } /** @@ -542,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); } /** @@ -551,17 +556,18 @@ 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]); } /** * Constructs an array from the given elements. * - * @param a The elements to construct the array with. + * @param as The elements to construct the array with. * @return A new array of the given elements. */ - public static Array array(final A... a) { - return new Array(a); + @SafeVarargs + public static Array array(final A...as) { + return arrayArray(as); } /** @@ -571,7 +577,7 @@ public static Array array(final A... a) { * @return A wrapped array. */ static Array mkArray(final Object[] a) { - return new Array(a); + return new Array<>(a); } /** @@ -581,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}); } /** @@ -590,11 +596,7 @@ public static Array single(final A a) { * @return A function that wraps a given array. */ public static F> wrap() { - return new F>() { - public Array f(final A[] as) { - return array(as); - } - }; + return Array::array; } /** @@ -603,11 +605,7 @@ public Array f(final A[] as) { * @return A function that maps a given function across a given array. */ public static F, F, Array>> map() { - return curry(new F2, Array, Array>() { - public Array f(final F abf, final Array array) { - return array.map(abf); - } - }); + return curry((abf, array) -> array.map(abf)); } /** @@ -627,11 +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 new F>, Array>() { - public Array f(final Array> as) { - return join(as); - } - }; + return Array::join; } /** @@ -668,6 +662,11 @@ public boolean exists(final F f) { return false; } + @Override + public boolean equals(Object o) { + return Equal.equals0(Array.class, this, o, () -> Equal.arrayEqual(Equal.anyEqual())); + } + /** * Finds the first occurrence of an element that matches the given predicate or no value if no * elements match. @@ -698,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); @@ -718,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))); @@ -758,15 +757,7 @@ public Array> zip(final Array bs) { * @return A new array with the same length as this array. */ public Array> zipIndex() { - return zipWith(range(0, length()), new F>>() { - public F> f(final A a) { - return new F>() { - public P2 f(final Integer i) { - return p(a, i); - } - }; - } - }); + return zipWith(range(0, length()), a -> i -> p(a, i)); } /** @@ -774,37 +765,25 @@ public P2 f(final Integer i) { * * @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 java.util.ArrayList projection of this array. + */ + public ArrayList toJavaList() { + return new ArrayList<>(asJavaList()); } /** @@ -817,29 +796,47 @@ public static Array iterableArray(final Iterable i) { return iterableList(i).toArray(); } + /** + * Creates an Array from the iterator. + */ + public static Array iteratorArray(final Iterator i) { + return iterableArray(() -> i); + } + + /** + * Returns a copy of the underlying primitive array. + * Equivalent to array(A...) + * + * @return A copy of the underlying primitive array. + */ + @SafeVarargs + public static Array arrayArray(final A...as) { + return new Array<>(as); + } + /** * Transforms an array of pairs into an array of first components and an array of second components. * * @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); } /** * Projects an array by providing only operations which do not mutate. */ - public final class ImmutableProjection implements Iterable { + public static final class ImmutableProjection implements Iterable { private final Array a; private ImmutableProjection(final Array a) { @@ -909,7 +906,7 @@ public Option toOption() { * @param x The value to return in left if this array is empty. * @return An either projection of this array. */ - public Either toEither(final P1 x) { + public Either toEither(final F0 x) { return a.toEither(x); } @@ -1048,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()); } @@ -1063,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/core/src/main/java/fj/data/Collectors.java b/core/src/main/java/fj/data/Collectors.java new file mode 100644 index 00000000..5ee9ead6 --- /dev/null +++ b/core/src/main/java/fj/data/Collectors.java @@ -0,0 +1,36 @@ +package fj.data; + +import java.util.stream.Collector; + +public final class Collectors { + + private Collectors() { + } + + public static Collector, List> toList() { + return Collector.of( + List.Buffer::new, + List.Buffer::snoc, + (acc1, acc2) -> acc1.append(acc2.toList()), + List.Buffer::toList + ); + } + + public static Collector, Array> toArray() { + return Collector.of( + List.Buffer::new, + List.Buffer::snoc, + (acc1, acc2) -> acc1.append(acc2.toList()), + (buf) -> buf.toList().toArray() + ); + } + + public static Collector, Stream> toStream() { + return Collector.of( + List.Buffer::new, + List.Buffer::snoc, + (acc1, acc2) -> acc1.append(acc2.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 2b4a51ca..403ca6d1 100644 --- a/core/src/main/java/fj/data/Conversions.java +++ b/core/src/main/java/fj/data/Conversions.java @@ -36,11 +36,7 @@ private Conversions() { * @return A function that converts lists to arrays. */ public static F, Array> List_Array() { - return new F, Array>() { - public Array f(final List as) { - return as.toArray(); - } - }; + return List::toArray; } /** @@ -49,11 +45,7 @@ public Array f(final List as) { * @return A function that converts lists to streams. */ public static F, Stream> List_Stream() { - return new F, Stream>() { - public Stream f(final List as) { - return as.toStream(); - } - }; + return List::toStream; } /** @@ -62,11 +54,7 @@ public Stream f(final List as) { * @return A function that converts lists to options. */ public static F, Option> List_Option() { - return new F, Option>() { - public Option f(final List as) { - return as.toOption(); - } - }; + return List::headOption; } /** @@ -75,43 +63,23 @@ public Option f(final List as) { * @return A function that converts lists to eithers. */ public static F, F, Either>> List_Either() { - return new F, F, Either>>() { - public F, Either> f(final P1 a) { - return new F, Either>() { - public Either f(final List bs) { - return bs.toEither(a); - } - }; - } - }; + return a -> bs -> bs.toEither(a); } /** * A function that converts lists to strings. */ - public static final F, String> List_String = new F, String>() { - public String f(final List cs) { - return asString(cs); - } - }; + public static final F, String> List_String = List::asString; /** * A function that converts lists to string buffers. */ - public static final F, StringBuffer> List_StringBuffer = new F, StringBuffer>() { - public StringBuffer f(final List cs) { - return new StringBuffer(asString(cs)); - } - }; + public static final F, StringBuffer> List_StringBuffer = cs -> new StringBuffer(asString(cs)); /** * A function that converts lists to string builders. */ - public static final F, StringBuilder> List_StringBuilder = new F, StringBuilder>() { - public StringBuilder f(final List cs) { - return new StringBuilder(asString(cs)); - } - }; + public static final F, StringBuilder> List_StringBuilder = cs -> new StringBuilder(asString(cs)); // END List -> @@ -123,11 +91,7 @@ public StringBuilder f(final List cs) { * @return A function that converts arrays to lists. */ public static F, List> Array_List() { - return new F, List>() { - public List f(final Array as) { - return as.toList(); - } - }; + return Array::toList; } /** @@ -136,11 +100,7 @@ public List f(final Array as) { * @return A function that converts arrays to streams. */ public static F, Stream> Array_Stream() { - return new F, Stream>() { - public Stream f(final Array as) { - return as.toStream(); - } - }; + return Array::toStream; } /** @@ -149,11 +109,7 @@ public Stream f(final Array as) { * @return A function that converts arrays to options. */ public static F, Option> Array_Option() { - return new F, Option>() { - public Option f(final Array as) { - return as.toOption(); - } - }; + return Array::toOption; } /** @@ -162,58 +118,35 @@ public Option f(final Array as) { * @return A function that converts arrays to eithers. */ public static F, F, Either>> Array_Either() { - return new F, F, Either>>() { - public F, Either> f(final P1 a) { - return new F, Either>() { - public Either f(final Array bs) { - return bs.toEither(a); - } - }; - } - }; + return a -> bs -> bs.toEither(a); } /** * A function that converts arrays to strings. */ - public static final F, String> Array_String = new F, String>() { - public String f(final Array cs) { - final StringBuilder sb = new StringBuilder(); - cs.foreach(new Effect1() { - public void f(final Character c) { - sb.append(c); - } - }); + public static final F, String> Array_String = cs -> { + final StringBuilder sb = new StringBuilder(cs.length()); + cs.foreachDoEffect(sb::append); return sb.toString(); - } }; /** * A function that converts arrays to string buffers. */ - public static final F, StringBuffer> Array_StringBuffer = new F, StringBuffer>() { - public StringBuffer f(final Array cs) { - final StringBuffer sb = new StringBuffer(); - cs.foreach(new Effect1() { - public void f(final Character c) { - sb.append(c); - } - }); + public static final F, StringBuffer> Array_StringBuffer = cs -> { + final StringBuffer sb = new StringBuffer(cs.length()); + cs.foreachDoEffect(sb::append); return sb; - } }; /** * A function that converts arrays to string builders. */ - public static final F, StringBuilder> Array_StringBuilder = - new F, StringBuilder>() { - public StringBuilder f(final Array cs) { - final StringBuilder sb = new StringBuilder(); - cs.foreach((Character c) -> sb.append(c)); - return sb; - } - }; + public static final F, StringBuilder> Array_StringBuilder = cs -> { + final StringBuilder sb = new StringBuilder(cs.length()); + cs.foreachDoEffect(sb::append); + return sb; + }; // END Array -> @@ -225,11 +158,7 @@ public StringBuilder f(final Array cs) { * @return A function that converts streams to lists. */ public static F, List> Stream_List() { - return new F, List>() { - public List f(final Stream as) { - return as.toList(); - } - }; + return Stream::toList; } /** @@ -238,11 +167,7 @@ public List f(final Stream as) { * @return A function that converts streams to arrays. */ public static F, Array> Stream_Array() { - return new F, Array>() { - public Array f(final Stream as) { - return as.toArray(); - } - }; + return Stream::toArray; } /** @@ -251,11 +176,7 @@ public Array f(final Stream as) { * @return A function that converts streams to options. */ public static F, Option> Stream_Option() { - return new F, Option>() { - public Option f(final Stream as) { - return as.toOption(); - } - }; + return Stream::toOption; } /** @@ -264,51 +185,35 @@ public Option f(final Stream as) { * @return A function that converts streams to eithers. */ public static F, F, Either>> Stream_Either() { - return new F, F, Either>>() { - public F, Either> f(final P1 a) { - return new F, Either>() { - public Either f(final Stream bs) { - return bs.toEither(a); - } - }; - } - }; + return a -> bs -> bs.toEither(a); } /** * A function that converts streams to strings. */ - public static final F, String> Stream_String = new F, String>() { - public String f(final Stream cs) { + public static final F, String> Stream_String = cs -> { final StringBuilder sb = new StringBuilder(); - cs.foreach((Character c) -> sb.append(c)); + cs.foreachDoEffect(sb::append); return sb.toString(); - } - }; + }; /** * A function that converts streams to string buffers. */ - public static final F, StringBuffer> Stream_StringBuffer = - new F, StringBuffer>() { - public StringBuffer f(final Stream cs) { - final StringBuffer sb = new StringBuffer(); - cs.foreach((Character c) -> sb.append(c)); - return sb; - } - }; + public static final F, StringBuffer> Stream_StringBuffer = cs -> { + final StringBuffer sb = new StringBuffer(); + cs.foreachDoEffect(sb::append); + return sb; + }; /** * A function that converts streams to string builders. */ - public static final F, StringBuilder> Stream_StringBuilder = - new F, StringBuilder>() { - public StringBuilder f(final Stream cs) { - final StringBuilder sb = new StringBuilder(); - cs.foreach((Character c) -> sb.append(c)); - return sb; - } - }; + public static final F, StringBuilder> Stream_StringBuilder = cs -> { + final StringBuilder sb = new StringBuilder(); + cs.foreachDoEffect(sb::append); + return sb; + }; // END Stream -> @@ -320,11 +225,7 @@ public StringBuilder f(final Stream cs) { * @return A function that converts options to lists. */ public static F, List> Option_List() { - return new F, List>() { - public List f(final Option o) { - return o.toList(); - } - }; + return Option::toList; } /** @@ -333,11 +234,7 @@ public List f(final Option o) { * @return A function that converts options to arrays. */ public static F, Array> Option_Array() { - return new F, Array>() { - public Array f(final Option o) { - return o.toArray(); - } - }; + return Option::toArray; } /** @@ -346,11 +243,7 @@ public Array f(final Option o) { * @return A function that converts options to streams. */ public static F, Stream> Option_Stream() { - return new F, Stream>() { - public Stream f(final Option o) { - return o.toStream(); - } - }; + return Option::toStream; } /** @@ -359,52 +252,30 @@ public Stream f(final Option o) { * @return A function that converts options to eithers. */ public static F, F, Either>> Option_Either() { - return new F, F, Either>>() { - public F, Either> f(final P1 a) { - return new F, Either>() { - public Either f(final Option o) { - return o.toEither(a); - } - }; - } - }; + return a -> o -> o.toEither(a); } /** * A function that converts options to strings. */ - public static final F, String> Option_String = new F, String>() { - public String f(final Option o) { - return asString(o.toList()); - } - }; + public static final F, String> Option_String = o -> asString(o.toList()); /** * A function that converts options to string buffers. */ - public static final F, StringBuffer> Option_StringBuffer = - new F, StringBuffer>() { - public StringBuffer f(final Option o) { - return new StringBuffer(asString(o.toList())); - } - }; + public static final F, StringBuffer> Option_StringBuffer = o -> new StringBuffer(asString(o.toList())); /** * A function that converts options to string builders. */ - public static final F, StringBuilder> Option_StringBuilder = - new F, StringBuilder>() { - public StringBuilder f(final Option o) { - return new StringBuilder(asString(o.toList())); - } - }; + public static final F, StringBuilder> Option_StringBuilder = o -> new StringBuilder(asString(o.toList())); // END Option -> // BEGIN Effect public static F> Effect0_P1() { - return e -> Effect0_P1(e); + return Conversions::Effect0_P1; } public static P1 Effect0_P1(Effect0 e) { @@ -416,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) { @@ -438,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,11 +322,7 @@ public static F> Effect_SafeIO() { * @return A function that converts eithers to lists. */ public static F, List> Either_ListA() { - return new F, List>() { - public List f(final Either e) { - return e.left().toList(); - } - }; + return e -> e.left().toList(); } /** @@ -464,11 +331,7 @@ public List f(final Either e) { * @return A function that converts eithers to lists. */ public static F, List> Either_ListB() { - return new F, List>() { - public List f(final Either e) { - return e.right().toList(); - } - }; + return e -> e.right().toList(); } /** @@ -477,11 +340,7 @@ public List f(final Either e) { * @return A function that converts eithers to arrays. */ public static F, Array> Either_ArrayA() { - return new F, Array>() { - public Array f(final Either e) { - return e.left().toArray(); - } - }; + return e -> e.left().toArray(); } /** @@ -490,11 +349,7 @@ public Array f(final Either e) { * @return A function that converts eithers to arrays. */ public static F, Array> Either_ArrayB() { - return new F, Array>() { - public Array f(final Either e) { - return e.right().toArray(); - } - }; + return e -> e.right().toArray(); } /** @@ -503,11 +358,7 @@ public Array f(final Either e) { * @return A function that converts eithers to streams. */ public static F, Stream> Either_StreamA() { - return new F, Stream>() { - public Stream f(final Either e) { - return e.left().toStream(); - } - }; + return e -> e.left().toStream(); } /** @@ -516,11 +367,7 @@ public Stream f(final Either e) { * @return A function that converts eithers to streams. */ public static F, Stream> Either_StreamB() { - return new F, Stream>() { - public Stream f(final Either e) { - return e.right().toStream(); - } - }; + return e -> e.right().toStream(); } /** @@ -529,11 +376,7 @@ public Stream f(final Either e) { * @return A function that converts eithers to options. */ public static F, Option> Either_OptionA() { - return new F, Option>() { - public Option f(final Either e) { - return e.left().toOption(); - } - }; + return e -> e.left().toOption(); } /** @@ -542,11 +385,7 @@ public Option f(final Either e) { * @return A function that converts eithers to options. */ public static F, Option> Either_OptionB() { - return new F, Option>() { - public Option f(final Either e) { - return e.right().toOption(); - } - }; + return e -> e.right().toOption(); } /** @@ -555,11 +394,7 @@ public Option f(final Either e) { * @return A function that converts eithers to strings. */ public static F, String> Either_StringA() { - return new F, String>() { - public String f(final Either e) { - return asString(e.left().toList()); - } - }; + return e -> asString(e.left().toList()); } /** @@ -568,11 +403,7 @@ public String f(final Either e) { * @return A function that converts eithers to strings. */ public static F, String> Either_StringB() { - return new F, String>() { - public String f(final Either e) { - return asString(e.right().toList()); - } - }; + return e -> asString(e.right().toList()); } /** @@ -581,11 +412,7 @@ public String f(final Either e) { * @return A function that converts eithers to string buffers. */ public static F, StringBuffer> Either_StringBufferA() { - return new F, StringBuffer>() { - public StringBuffer f(final Either e) { - return new StringBuffer(asString(e.left().toList())); - } - }; + return e -> new StringBuffer(asString(e.left().toList())); } /** @@ -594,11 +421,7 @@ public StringBuffer f(final Either e) { * @return A function that converts eithers to string buffers. */ public static F, StringBuffer> Either_StringBufferB() { - return new F, StringBuffer>() { - public StringBuffer f(final Either e) { - return new StringBuffer(asString(e.right().toList())); - } - }; + return e -> new StringBuffer(asString(e.right().toList())); } /** @@ -607,11 +430,7 @@ public StringBuffer f(final Either e) { * @return A function that converts eithers to string builders. */ public static F, StringBuilder> Either_StringBuilderA() { - return new F, StringBuilder>() { - public StringBuilder f(final Either e) { - return new StringBuilder(asString(e.left().toList())); - } - }; + return e -> new StringBuilder(asString(e.left().toList())); } /** @@ -620,11 +439,7 @@ public StringBuilder f(final Either e) { * @return A function that converts eithers to string builders. */ public static F, StringBuilder> Either_StringBuilderB() { - return new F, StringBuilder>() { - public StringBuilder f(final Either e) { - return new StringBuilder(asString(e.right().toList())); - } - }; + return e -> new StringBuilder(asString(e.right().toList())); } // END Either -> @@ -636,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 @@ -646,29 +461,17 @@ public static F, SafeIO> F_SafeIO() { /** * A function that converts strings to lists. */ - public static final F> String_List = new F>() { - public List f(final String s) { - return fromString(s); - } - }; + public static final F> String_List = List::fromString; /** * A function that converts strings to arrays. */ - public static final F> String_Array = new F>() { - public Array f(final String s) { - return fromString(s).toArray(); - } - }; + public static final F> String_Array = s -> fromString(s).toArray(); /** * A function that converts strings to options. */ - public static final F> String_Option = new F>() { - public Option f(final String s) { - return fromString(s).toOption(); - } - }; + public static final F> String_Option = s -> fromString(s).headOption(); /** * A function that converts string to eithers. @@ -676,43 +479,23 @@ public Option f(final String s) { * @return A function that converts string to eithers. */ public static F, F>> String_Either() { - return new F, F>>() { - public F> f(final P1 a) { - return new F>() { - public Either f(final String s) { - return fromString(s).toEither(a); - } - }; - } - }; + return a -> s -> fromString(s).toEither(a); } /** * A function that converts strings to streams. */ - public static final F> String_Stream = new F>() { - public Stream f(final String s) { - return fromString(s).toStream(); - } - }; + public static final F> String_Stream = s -> fromString(s).toStream(); /** * A function that converts strings to string buffers. */ - public static final F String_StringBuffer = new F() { - public StringBuffer f(final String s) { - return 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 = new F() { - public StringBuilder f(final String s) { - return new StringBuilder(s); - } - }; + public static final F String_StringBuilder = StringBuilder::new; // END String -> @@ -721,40 +504,22 @@ public StringBuilder f(final String s) { /** * A function that converts string buffers to lists. */ - public static final F> StringBuffer_List = new F>() { - public List f(final StringBuffer s) { - return fromString(s.toString()); - } - }; + public static final F> StringBuffer_List = s -> fromString(s.toString()); /** * A function that converts string buffers to arrays. */ - public static final F> StringBuffer_Array = new F>() { - public Array f(final StringBuffer s) { - return fromString(s.toString()).toArray(); - } - }; + public static final F> StringBuffer_Array = s -> fromString(s.toString()).toArray(); /** * A function that converts string buffers to streams. */ - public static final F> StringBuffer_Stream = - new F>() { - public Stream f(final StringBuffer s) { - return fromString(s.toString()).toStream(); - } - }; + public static final F> StringBuffer_Stream = s -> fromString(s.toString()).toStream(); /** * A function that converts string buffers to options. */ - public static final F> StringBuffer_Option = - new F>() { - public Option f(final StringBuffer s) { - return fromString(s.toString()).toOption(); - } - }; + public static final F> StringBuffer_Option = s -> fromString(s.toString()).headOption(); /** * A function that converts string buffers to eithers. @@ -762,34 +527,18 @@ public Option f(final StringBuffer s) { * @return A function that converts string buffers to eithers. */ public static F, F>> StringBuffer_Either() { - return new F, F>>() { - public F> f(final P1 a) { - return new F>() { - public Either f(final StringBuffer s) { - return fromString(s.toString()).toEither(a); - } - }; - } - }; + return a -> s -> fromString(s.toString()).toEither(a); } /** * A function that converts string buffers to strings. */ - public static final F StringBuffer_String = new F() { - public String f(final StringBuffer s) { - return 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 = new F() { - public StringBuilder f(final StringBuffer s) { - return new StringBuilder(s); - } - }; + public static final F StringBuffer_StringBuilder = StringBuilder::new; // END StringBuffer -> @@ -798,41 +547,22 @@ public StringBuilder f(final StringBuffer s) { /** * A function that converts string builders to lists. */ - public static final F> StringBuilder_List = new F>() { - public List f(final StringBuilder s) { - return fromString(s.toString()); - } - }; + public static final F> StringBuilder_List = s -> fromString(s.toString()); /** * A function that converts string builders to arrays. */ - public static final F> StringBuilder_Array = - new F>() { - public Array f(final StringBuilder s) { - return fromString(s.toString()).toArray(); - } - }; + public static final F> StringBuilder_Array = s -> fromString(s.toString()).toArray(); /** * A function that converts string builders to streams. */ - public static final F> StringBuilder_Stream = - new F>() { - public Stream f(final StringBuilder s) { - return fromString(s.toString()).toStream(); - } - }; + public static final F> StringBuilder_Stream = s -> fromString(s.toString()).toStream(); /** * A function that converts string builders to options. */ - public static final F> StringBuilder_Option = - new F>() { - public Option f(final StringBuilder s) { - return fromString(s.toString()).toOption(); - } - }; + public static final F> StringBuilder_Option = s -> fromString(s.toString()).headOption(); /** * A function that converts string builders to eithers. @@ -840,34 +570,18 @@ public Option f(final StringBuilder s) { * @return A function that converts string builders to eithers. */ public static F, F>> StringBuilder_Either() { - return new F, F>>() { - public F> f(final P1 a) { - return new F>() { - public Either f(final StringBuilder s) { - return fromString(s.toString()).toEither(a); - } - }; - } - }; + return a -> s -> fromString(s.toString()).toEither(a); } /** * A function that converts string builders to strings. */ - public static final F StringBuilder_String = new F() { - public String f(final StringBuilder s) { - return 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 = new F() { - public StringBuffer f(final StringBuilder s) { - return new StringBuffer(s); - } - }; + public static final F StringBuilder_StringBuffer = StringBuffer::new; // END StringBuilder -> @@ -879,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) { @@ -895,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 new file mode 100644 index 00000000..597b6dda --- /dev/null +++ b/core/src/main/java/fj/data/DList.java @@ -0,0 +1,131 @@ +package fj.data; + +import fj.F; +import fj.P; +import fj.control.Trampoline; + +import java.util.Iterator; + +import static fj.data.List.iterableList; + +/** + * Difference List. It converts left associative appends into right associative ones to improve performance. + * + * @version %build.number% + */ +public final class DList { + private final F,Trampoline>> appendFn; + + private DList(final F,Trampoline>> appendFn) { + this.appendFn = appendFn; + } + + /** + * Creates a DList from the function + * + * For alternatives functions to create a DList: + * @see #iterableDList + * @see #iteratorDList + * @see #arrayDList + */ + public static DList dlist(final F,Trampoline>> f) { + return new DList<>(f); + } + + /** + * Creates a DList from a List + */ + public static DList listDList(final List a) { + return dlist((List tail) -> Trampoline.pure(a.append(tail))); + } + + /** + * Creates a DList from an Iterable + */ + public static DList iterableDList(final Iterable it) { + return listDList(iterableList(it)); + } + + /** + * Creates a DList from an Iterator + */ + public static DList iteratorDList(final Iterator it) { + return iterableDList(() -> it); + } + + /** + * Creates a DList from an array + */ + @SafeVarargs + public static DList arrayDList(final A...as) { + return listDList(List.list(as)); + } + + /** + * Concatenates all the internal Lists together that are held in + * the DList's lambda's state to produce a List. + * This is what converts the appending operation from left associative to right associative, + * giving DList it's speed. + * @return the final List + */ + public List run() { + return appendFn.f(List.nil()).run(); + } + + /** + * Converts the DList to a standard java.util.List. + */ + public java.util.List toJavaList() { + return run().toJavaList(); + } + + /** + * A empty DList. + * @param + * @return a empty DList. + */ + public static DList nil() { + return new DList<>(Trampoline.pure()); + } + + /** + * Produces a DList with one element. + * @param + * @param a the element in the DList. + * @return a DList with one element. + */ + public static DList single(A a) { + return new DList<>((List tail) -> Trampoline.pure(tail.cons(a))); + } + + /** + * Prepends a single element on the DList to produce a new DList. + * @param a the element to append. + * @return the new DList. + */ + public DList cons(A a) { + return single(a).append(this); + } + + /** + * Appends a single element on the end of the DList to produce a new DList. + * @param a the element to append. + * @return the new DList. + */ + public DList snoc(A a) { + return this.append(single(a)); + } + + /** + * Appends two DLists together to produce a new DList. + * @param other the other DList to append on the end of this one. + * @return the new DList. + */ + public DList append(DList other) { + return new DList<>(kleisliTrampCompose(this.appendFn, other.appendFn)); + } + + private static F> kleisliTrampCompose(F> bc, F> ab) { + 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 f656471d..e710c1d6 100644 --- a/core/src/main/java/fj/data/Either.java +++ b/core/src/main/java/fj/data/Either.java @@ -1,26 +1,18 @@ package fj.data; -import static fj.Bottom.error; -import fj.Effect; -import fj.F; -import static fj.Function.identity; -import static fj.P.p; - -import fj.Show; -import fj.Function; -import fj.P1; -import fj.Unit; +import fj.*; 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.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 @@ -41,7 +33,7 @@ private Either() { * @return A left projection of this either. */ public final LeftProjection left() { - return new LeftProjection(this); + return new LeftProjection<>(this); } /** @@ -50,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); } /** @@ -74,10 +66,27 @@ 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. + * + * @param left The function to map if this is left. + * @param right The function to map if this is right. + * @return A new either value after mapping with the appropriate function applied. + */ + public final Either bimap(final F left, final F right) { + return either(compose(left_(), left), compose(right_(), right)); + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Either.class, this, other, () -> Equal.eitherEqual(Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.eitherHash(Hash.anyHash(), Hash.anyHash()).hash(this); } /** @@ -86,7 +95,7 @@ public final X either(final F left, final F right) { * @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 { @@ -103,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 { @@ -119,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) { @@ -155,12 +174,12 @@ public Either either() { * @param err The error message to fail with. * @return The value of this projection */ - public A valueE(final P1 err) { + public A valueE(final F0 err) { if (e.isLeft()) //noinspection CastToConcreteClass return ((Left) e).a; else - throw error(err._1()); + throw error(err.f()); } /** @@ -188,8 +207,8 @@ public A value() { * @param a The value to return if this projection has no value. * @return The value of this projection or the given argument. */ - public A orValue(final P1 a) { - return isLeft() ? value() : a._1(); + public A orValue(final F0 a) { + return e.isLeft() ? value() : a.f(); } /** @@ -199,7 +218,7 @@ public A orValue(final P1 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; } /** @@ -211,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()); } /** @@ -221,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(); @@ -232,8 +251,8 @@ public Unit foreach(final F f) { * * @param f The side-effect to execute. */ - public void foreach(final Effect1 f) { - if (isLeft()) + public void foreachDoEffect(final Effect1 f) { + if (e.isLeft()) f.f(value()); } @@ -244,7 +263,7 @@ public void foreach(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()); } /** @@ -254,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()); } /** @@ -264,7 +283,44 @@ 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 e.isLeft() ? + f.f(value()).map(Either::left) : + list(right(e.right().value())); + } + + /** + * Anonymous bind through this projection. + * + * @param f the function to traverse with + * @return An either after traversing through this projection. + */ + public IO> traverseIO(final F> f) { + 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())); } /** @@ -276,11 +332,11 @@ public Either sequence(final Either e) { * 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(); } /** @@ -290,11 +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(new F, Either>() { - public Either f(final F f) { - return map(f); - } - }); + return e.left().bind(this::map); } /** @@ -306,7 +358,7 @@ public Either f(final F f) { * function to the value. */ public boolean forall(final F f) { - return isRight() || f.f(value()); + return e.isRight() || f.f(value()); } /** @@ -318,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()); } /** @@ -327,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(); } /** @@ -338,7 +390,7 @@ public List toList() { * None. */ public Option toOption() { - return isLeft() ? some(value()) : Option.none(); + return e.isLeft() ? some(value()) : Option.none(); } /** @@ -347,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); @@ -361,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(); } /** @@ -372,12 +424,24 @@ public Stream toStream() { public Collection toCollection() { return toList().toCollection(); } + + public Option> traverseOption(F> f) { + return e.isLeft() ? + f.f(value()).map(left_()) : + some(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) { @@ -408,12 +472,22 @@ public Either either() { * @param err The error message to fail with. * @return The value of this projection */ - public B valueE(final P1 err) { + public B valueE(final String err) { + return valueE(p(err)); + } + + /** + * 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 F0 err) { if (e.isRight()) //noinspection CastToConcreteClass return ((Right) e).b; else - throw error(err._1()); + throw error(err.f()); } /** @@ -425,14 +499,24 @@ 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. * * @param b The value to return if this projection has no value. * @return The value of this projection or the given argument. */ - public B orValue(final P1 b) { - return isRight() ? value() : b._1(); + public B orValue(final F0 b) { + return e.isRight() ? value() : b.f(); } /** @@ -444,7 +528,7 @@ public B orValue(final P1 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()); } /** @@ -454,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(); @@ -465,8 +549,8 @@ public Unit foreach(final F f) { * * @param f The side-effect to execute. */ - public void foreach(final Effect1 f) { - if (isRight()) + public void foreachDoEffect(final Effect1 f) { + if (e.isRight()) f.f(value()); } @@ -477,7 +561,7 @@ public void foreach(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()); } /** @@ -487,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. * @@ -498,9 +581,59 @@ 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 e.isRight() ? + f.f(value()).map(right_()) : + list(left(e.left().value())); + } + + /** + * Traverse with a function that has IO effect + * + * @param f the function to traverse with + * @return An either after traversing through this projection. + */ + public IO> traverseIO(final F> f) { + 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 e.isRight() ? + f.f(value()).map(right_()) : + p(left(e.left().value())); + } + + /** + * Traverse this right with the given function and collect the output as an option. + * + * @param f the given function + * @param the type of the option value + * @return the option + */ + public Option> traverseOption(final F> f) { + return e.isRight() ? + f.f(value()).map(right_()) : + some(left(e.left().value())); + } + /** * Returns None if this projection has no value or if the given predicate * p does not hold for the value, otherwise, returns a left in Some. @@ -510,11 +643,11 @@ public Either sequence(final Either e) { * 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(); } /** @@ -524,11 +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(new F, Either>() { - public Either f(final F f) { - return map(f); - } - }); + return e.right().bind(this::map); } /** @@ -540,7 +669,7 @@ public Either f(final F f) { * function to the value. */ public boolean forall(final F f) { - return isLeft() || f.f(value()); + return e.isLeft() || f.f(value()); } /** @@ -552,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()); } /** @@ -561,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(); } /** @@ -572,7 +701,7 @@ public List toList() { * None. */ public Option toOption() { - return isRight() ? some(value()) : Option.none(); + return e.isRight() ? some(value()) : Option.none(); } /** @@ -581,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); @@ -595,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(); } /** @@ -606,6 +735,13 @@ public Stream toStream() { public Collection toCollection() { return toList().toCollection(); } + + public Stream> traverseStream(F> f) { + return e.isRight() ? + f.f(value()).map(Either::right) : + Stream.single(left(e.left().value())); + + } } /** @@ -615,7 +751,7 @@ public Collection toCollection() { * @return A left value of either. */ public static Either left(final A a) { - return new Left(a); + return new Left<>(a); } /** @@ -624,11 +760,7 @@ public static Either left(final A a) { * @return A function that constructs a left value of either. */ public static F> left_() { - return new F>() { - public Either f(final A a) { - return left(a); - } - }; + return Either::left; } /** @@ -637,11 +769,7 @@ public Either f(final A a) { * @return A function that constructs a right value of either. */ public static F> right_() { - return new F>() { - public Either f(final B b) { - return right(b); - } - }; + return Either::right; } /** @@ -651,37 +779,64 @@ public Either f(final B b) { * @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_() { - return new F, F, Either>>() { - public F, Either> f(final F axf) { - return new F, Either>() { - public Either f(final Either e) { - return e.left().map(axf); - } - }; - } - }; + return axf -> e -> e.left().map(axf); } /** + * 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_() { - return new F, F, Either>>() { - public F, Either> f(final F axf) { - return new F, Either>() { - public Either f(final Either e) { - return e.right().map(axf); - } - }; - } - }; + return axf -> e -> e.right().map(axf); } /** @@ -691,8 +846,7 @@ public Either f(final Either e) { * @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()); } /** @@ -702,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()); } /** @@ -711,15 +864,12 @@ 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()) : - a.head().left().bind(new F, X>>() { - public Either, X> f(final A aa) { - return sequenceLeft(a.tail()).left().map(cons_(aa)); - } - }); + left(List.nil()) : + a.head().left().bind(aa -> sequenceLeft(a.tail()).left().map(cons_(aa))); } /** @@ -727,15 +877,85 @@ public Either, X> f(final A aa) { * * @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()) : - a.head().right().bind(new F>>() { - public Either> f(final B bb) { - return sequenceRight(a.tail()).right().map(cons_(bb)); - } - }); + right(List.nil()) : + a.head().right().bind(bb -> sequenceRight(a.tail()).right().map(cons_(bb))); + } + + /** + * Traversable instance of RightProjection of Either for List. + * + * @return traversed value + */ + public final List> traverseListRight(final F> f) { + return right().traverseList(f); + } + + /** + * Traversable instance of LeftProjection of Either for List. + * + * @return traversed value + */ + public final List> traverseListLeft(final F> f) { + return left().traverseList(f); + } + + /** + * Traversable instance of RightProjection of Either for IO. + * + * @return traversed value + */ + public final IO> traverseIORight(final F> f) { + return right().traverseIO(f); + } + + /** + * Traversable instance of LeftProjection of Either for IO. + * + * @return traversed value + */ + public final IO> traverseIOLeft(final F> f) { + return left().traverseIO(f); + } + + /** + * Traversable instance of RightProjection of Either for Option. + * + * @return traversed value + */ + public final Option> traverseOptionRight(final F> f) { + return right().traverseOption(f); + } + + /** + * Traversable instance of LeftProjection of Either for Option. + * + * @return traversed value + */ + public final Option> traverseOptionLeft(final F> f) { + return left().traverseOption(f); + } + + /** + * Traversable instance of RightProjection of Either for Stream. + * + * @return traversed value + */ + public final Stream> traverseStreamRight(final F> f) { + return right().traverseStream(f); + } + + /** + * Traversable instance of LeftProjection of Either for Stream. + * + * @return traversed value + */ + public final Stream> traverseStreamLeft(final F> f) { + return left().traverseStream(f); } /** @@ -745,19 +965,19 @@ public Either> f(final B bb) { * @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. * @param left The left value to use if the condition does not satisfy. * @return A constructed either based on the given condition. */ - public static Either iif(final boolean c, final P1 right, final P1 left) { - return c ? new Right(right._1()) : new Left(left._1()); + public static Either iif(final boolean c, final F0 right, final F0 left) { + return c ? new Right<>(right.f()) : new Left<>(left.f()); } /** @@ -767,15 +987,7 @@ public static Either iif(final boolean c, final P1 right, final * @return All the left values in the given list. */ public static List lefts(final List> es) { - return es.foldRight(new F, F, List>>() { - public F, List> f(final Either e) { - return new F, List>() { - public List f(final List as) { - return 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()); } /** @@ -785,18 +997,10 @@ public List f(final List as) { * @return All the right values in the given list. */ public static List rights(final List> es) { - return es.foldRight(new F, F, List>>() { - public F, List> f(final Either e) { - return new F, List>() { - public List f(final List bs) { - return 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 67697ed4..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: *

* * @version %build.number% @@ -165,16 +161,12 @@ public Ord order() { * @return An enumerator after the given functions are applied. */ public Enumerator xmap(final F f, final F g) { - final F, Option> of = new F, Option>() { - public Option f(final Option o) { - return o.map(f); - } - }; + final F, Option> of = o -> o.map(f); return enumerator(compose(compose(of, successor), g), compose(compose(of, predecessor), g), max.map(f), min.map(f), - order.comap(g), + order.contramap(g), compose(compose(Function., Option>compose().f(of), plus), g)); } @@ -189,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. * @@ -224,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); } /** @@ -240,31 +244,29 @@ 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(new F2>() { - public Option f(final A a, final Long 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); } })); } @@ -272,175 +274,116 @@ else if (l < 0L) { /** * An enumerator for boolean. */ - public static final Enumerator booleanEnumerator = enumerator(new F>() { - public Option f(final Boolean b) { - return b ? Option.none() : some(true); - } - }, new F>() { - public Option f(final Boolean b) { - return b ? some(false) : Option.none(); - } - }, some(true), some(false), booleanOrd); + public static final Enumerator booleanEnumerator = enumerator( + b -> b ? Option.none() : some(true), + b -> b ? some(false) : Option.none(), + some(true), some(false), booleanOrd + ); /** * An enumerator for byte. */ - public static final Enumerator byteEnumerator = enumerator(new F>() { - public Option f(final Byte b) { - return b == Byte.MAX_VALUE ? Option.none() : some((byte) (b + 1)); - } - }, new F>() { - public Option f(final Byte b) { - return b == Byte.MIN_VALUE ? Option.none() : some((byte) (b - 1)); - } - }, some(Byte.MAX_VALUE), some(Byte.MIN_VALUE), byteOrd); + 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)), + some(Byte.MAX_VALUE), some(Byte.MIN_VALUE), byteOrd + ); /** * An enumerator for char. */ - public static final Enumerator charEnumerator = enumerator(new F>() { - public Option f(final Character c) { - return c == Character.MAX_VALUE ? Option.none() : some((char) (c + 1)); - } - }, new F>() { - public Option f(final Character c) { - return c == Character.MIN_VALUE ? Option.none() : some((char) (c - 1)); - } - }, some(Character.MAX_VALUE), some(Character.MIN_VALUE), charOrd); + 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)), + some(Character.MAX_VALUE), some(Character.MIN_VALUE), charOrd + ); /** * An enumerator for double. */ - public static final Enumerator doubleEnumerator = enumerator(new F>() { - public Option f(final Double d) { - return d == Double.MAX_VALUE ? Option.none() : some(d + 1D); - } - }, new F>() { - public Option f(final Double d) { - return d == Double.MIN_VALUE ? Option.none() : some(d - 1D); - } - }, some(Double.MAX_VALUE), some(Double.MIN_VALUE), doubleOrd); + 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), + some(Double.MAX_VALUE), some(Double.MIN_VALUE), doubleOrd + ); /** * An enumerator for float. */ - public static final Enumerator floatEnumerator = enumerator(new F>() { - public Option f(final Float f) { - return f == Float.MAX_VALUE ? Option.none() : some(f + 1F); - } - }, new F>() { - public Option f(final Float f) { - return f == Float.MIN_VALUE ? Option.none() : some(f - 1F); - } - }, some(Float.MAX_VALUE), some(Float.MIN_VALUE), floatOrd); + 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), + some(Float.MAX_VALUE), some(Float.MIN_VALUE), floatOrd + ); /** * An enumerator for int. */ - public static final Enumerator intEnumerator = enumerator(new F>() { - public Option f(final Integer i) { - return i == Integer.MAX_VALUE ? Option.none() : some(i + 1); - } - }, new F>() { - public Option f(final Integer i) { - return i == Integer.MIN_VALUE ? Option.none() : some(i - 1); - } - }, some(Integer.MAX_VALUE), some(Integer.MIN_VALUE), intOrd); + 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), + some(Integer.MAX_VALUE), some(Integer.MIN_VALUE), intOrd + ); /** * An enumerator for BigInteger. */ - public static final Enumerator bigintEnumerator = enumerator(new F>() { - public Option f(final BigInteger i) { - return some(i.add(BigInteger.ONE)); - } - }, new F>() { - public Option f(final BigInteger i) { - return some(i.subtract(BigInteger.ONE)); - } - }, Option.none(), Option.none(), bigintOrd, curry( - new F2>() { - public Option f(final BigInteger i, final Long l) { - return some(i.add(BigInteger.valueOf(l))); - } - })); + public static final Enumerator bigintEnumerator = enumerator( + i -> some(i.add(BigInteger.ONE)), + i -> some(i.subtract(BigInteger.ONE)), + Option.none(), Option.none(), bigintOrd, + curry((i, l) -> some(i.add(BigInteger.valueOf(l)))) + ); /** * An enumerator for BigDecimal. */ - public static final Enumerator bigdecimalEnumerator = enumerator(new F>() { - public Option f(final BigDecimal i) { - return some(i.add(BigDecimal.ONE)); - } - }, new F>() { - public Option f(final BigDecimal i) { - return some(i.subtract(BigDecimal.ONE)); - } - }, Option.none(), Option.none(), bigdecimalOrd, curry( - new F2>() { - public Option f(final BigDecimal d, final Long l) { - return some(d.add(BigDecimal.valueOf(l))); - } - })); + public static final Enumerator bigdecimalEnumerator = enumerator( + i -> some(i.add(BigDecimal.ONE)), + i -> some(i.subtract(BigDecimal.ONE)), + Option.none(), Option.none(), bigdecimalOrd, + curry((d, l) -> some(d.add(BigDecimal.valueOf(l)))) + ); /** * An enumerator for long. */ - public static final Enumerator longEnumerator = enumerator(new F>() { - public Option f(final Long i) { - return i == Long.MAX_VALUE ? Option.none() : some(i + 1L); - } - }, new F>() { - public Option f(final Long i) { - return i == Long.MIN_VALUE ? Option.none() : some(i - 1L); - } - }, some(Long.MAX_VALUE), some(Long.MIN_VALUE), longOrd); + 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), + some(Long.MAX_VALUE), some(Long.MIN_VALUE), longOrd + ); /** * An enumerator for short. */ - public static final Enumerator shortEnumerator = enumerator(new F>() { - public Option f(final Short i) { - return i == Short.MAX_VALUE ? Option.none() : some((short) (i + 1)); - } - }, new F>() { - public Option f(final Short i) { - return i == Short.MIN_VALUE ? Option.none() : some((short) (i - 1)); - } - }, some(Short.MAX_VALUE), some(Short.MIN_VALUE), shortOrd); + 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)), + some(Short.MAX_VALUE), some(Short.MIN_VALUE), shortOrd + ); /** * An enumerator for Ordering. */ - public static final Enumerator orderingEnumerator = enumerator(new F>() { - public Option f(final Ordering o) { - return o == LT ? some(EQ) : o == EQ ? some(GT) : Option.none(); - } - }, new F>() { - public Option f(final Ordering o) { - return o == GT ? some(EQ) : o == EQ ? some(LT) : Option.none(); - } - }, some(GT), some(LT), orderingOrd); + 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(), + some(GT), some(LT), orderingOrd + ); /** * An enumerator for Natural */ - public static final Enumerator naturalEnumerator = enumerator(new F>() { - public Option f(final Natural n) { - return Option.some(n.succ()); - } - }, new F>() { - public Option f(final Natural n) { - return n.pred(); - } - }, Option.none(), some(Natural.ZERO), naturalOrd, curry(new F2>() { - public Option f(final Natural n, final Long l) { - return some(n).apply(Natural.natural(l).map(Function.curry(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return n1.add(n2); - } - }))); - } - })); + public static final Enumerator naturalEnumerator = enumerator( + n -> some(n.succ()), + Natural::pred, + Option.none(), some(Natural.ZERO), naturalOrd, + curry((n, l) -> + some(n).apply( + 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 83ab9c79..455c529c 100755 --- a/core/src/main/java/fj/data/HashMap.java +++ b/core/src/main/java/fj/data/HashMap.java @@ -5,8 +5,10 @@ import java.util.Collection; import java.util.Iterator; +import java.util.Map; import static fj.P.p; +import static fj.data.List.iterableList; import static fj.data.Option.fromNull; /** @@ -16,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() { @@ -50,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; @@ -62,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()); } } @@ -82,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()); } /** @@ -100,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; } @@ -111,9 +105,16 @@ public HashMap(final Equal e, final Hash h, final int initialCapacity, fin * @return A new hash map that uses {@link Object#equals} and {@link Object#hashCode}. */ public static HashMap hashMap() { - final Equal e = Equal.anyEqual(); - final Hash h = Hash.anyHash(); - return new HashMap(e, h); + return hashMap(Equal.anyEqual(), Hash.anyHash()); + } + + /** + * Construct a hash map. + * + * @return A new hash map. + */ + public static HashMap hashMap(final Equal e, final Hash h) { + return new HashMap<>(e, h); } /** @@ -144,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))); } /** @@ -153,11 +154,7 @@ public Option get(final K k) { * @return A curried version of {@link #get(Object)}. */ public F> get() { - return new F>() { - public Option f(final K k) { - return get(k); - } - }; + return this::get; } /** @@ -174,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)); } /** @@ -183,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(); @@ -198,11 +195,7 @@ public List keys() { * @return All values in this hash map. */ public List values() { - return keys().map(new F() { - public V f(final K k) { - return 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,31 +261,31 @@ 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 foreach(Effect1> effect) { - toStream().foreach(effect); + public void foreachDoEffect(Effect1> effect) { + toStream().foreachDoEffect(effect); } public void foreach(F, Unit> function) { @@ -300,11 +293,7 @@ public void foreach(F, Unit> function) { } public List> toList() { - return keys().map(new F>() { - public P2 f(final K k) { - return p(k, get(k).some()); - } - }); + return keys().map(k -> p(k, get(k).some())); } /** @@ -321,7 +310,7 @@ public Stream> toStream() { } public Option> toOption() { - return toList().toOption(); + return toList().headOption(); } public Array> toArray() { @@ -329,22 +318,71 @@ 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; } - public static HashMap from(Iterable> entries) { - return from(entries, Equal.anyEqual(), Hash.anyHash()); + public static HashMap fromMap(java.util.Map map) { + return fromMap(Equal.anyEqual(), Hash.anyHash(), map); } - public static HashMap from(Iterable> entries, Equal equal, Hash hash) { - final HashMap map = new HashMap(equal, hash); + public static HashMap fromMap(Equal eq, Hash h, java.util.Map map) { + 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 + */ + public static HashMap iterableHashMap(final Equal equal, final Hash hash, final Iterable> entries) { + final HashMap map = new HashMap<>(equal, hash); for (P2 entry : entries) { - map.set(entry._1(), entry._2()); + map.set(entry._1(), entry._2()); } return map; } + + /** + * Converts the Iterable to a HashMap + */ + public static HashMap iterableHashMap(final Iterable> entries) { + return iterableHashMap(Equal.anyEqual(), Hash.anyHash(), entries); + } + + /** + * Converts the array to a HashMap + */ + @SafeVarargs + public static HashMap arrayHashMap(final P2...entries) { + return iterableHashMap(Array.array(entries)); + } + + /** + * Converts the array to a HashMap + */ + @SafeVarargs + public static HashMap arrayHashMap(final Equal equal, final Hash hash, final P2...entries) { + return iterableHashMap(equal, hash, Array.array(entries)); + } + + /** + * Converts the Iterator to a HashMap + */ + public static HashMap iteratorHashMap(final Equal equal, final Hash hash, final Iterator> entries) { + return iterableHashMap(equal, hash, () -> entries); + } + + /** + * Converts the Iterator to a HashMap + */ + public static HashMap iteratorHashMap(final Iterator> entries) { + return iterableHashMap(() -> entries); + } + } diff --git a/core/src/main/java/fj/data/HashSet.java b/core/src/main/java/fj/data/HashSet.java index 8228eeab..d7b2c832 100644 --- a/core/src/main/java/fj/data/HashSet.java +++ b/core/src/main/java/fj/data/HashSet.java @@ -3,6 +3,9 @@ import fj.Equal; import fj.Hash; import fj.Unit; + +import static fj.Equal.anyEqual; +import static fj.Hash.anyHash; import static fj.Unit.unit; import java.util.Collection; @@ -33,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); } /** @@ -44,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); } /** @@ -56,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); } /** @@ -80,6 +83,84 @@ public int hash(final A a) { return m.hash(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); + } + + /** + * Creates an empty HashSet + */ + public static HashSet empty() { + return empty(anyEqual(), anyHash()); + } + + /** + * Create a HashSet from the Iterable. + */ + public static HashSet iterableHashSet(final Iterable it) { + return iterableHashSet(anyEqual(), anyHash(), it); + } + + /** + * Create a HashSet from the Iterable. + */ + public static HashSet iterableHashSet(final Equal e, final Hash h, final Iterable it) { + final HashSet hs = empty(e, h); + for (A a: it) { + hs.set(a); + } + return hs; + } + + /** + * Create a HashSet from the Iterator. + */ + public static HashSet iteratorHashSet(final Iterator it) { + return iterableHashSet(() -> it); + } + + /** + * Create a HashSet from the Iterator. + */ + public static HashSet iteratorHashSet(final Equal e, final Hash h, final Iterator it) { + return iterableHashSet(e, h, () -> it); + } + + /** + * Create a HashSet from the array. + */ + @SafeVarargs + public static HashSet arrayHashSet(final A...as) { + return iterableHashSet(Array.array(as)); + } + + /** + * Create a HashSet from the array. + */ + @SafeVarargs + public static HashSet arrayHashSet(final Equal e, final Hash h, final A...as) { + return iterableHashSet(e, h, Array.array(as)); + } + + /** + * Create a HashSet from the array. + */ + @SafeVarargs + public static HashSet hashSet(final A...as) { + return arrayHashSet(as); + } + + /** + * Create a HashSet from the array. + */ + @SafeVarargs + public static HashSet hashSet(final Equal e, final Hash h, final A...as) { + return arrayHashSet(e, h, as); + } + /** * Determines if this hash set contains the given element. * @@ -144,6 +225,18 @@ public List toList() { return m.keys(); } + public java.util.List toJavaList() { + return toList().toJavaList(); + } + + public java.util.Set toJavaSet() { + return new java.util.HashSet<>(toCollection()); + } + + public static HashSet fromSet(java.util.Set s) { + return iterableHashSet(s); + } + /** * Projects an immutable collection of this hash set. * 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 7755b8fe..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 }, @@ -25,419 +35,440 @@ * without indirection in between). * * @author Martin Grotzke - * */ -public class IOFunctions { - - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; +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) { return Try.f(toTry(io)); } - public static IO io(P1 p) { - return () -> p._1(); - } - - public static IO io(Try0 t) { - return () -> t.f(); - } - - public static final F> closeReader = - new F>() { - @Override - public IO f(final Reader r) { - return closeReader(r); - } - }; - - public static IO closeReader(final Reader r) { - return new IO() { - @Override - public Unit run() throws IOException { - r.close(); - return Unit.unit(); - } - }; - } - - /** - * An IO monad that reads lines from the given file (using a {@link BufferedReader}) and passes - * lines to the provided iteratee. May not be suitable for files with very long - * lines, consider to use {@link #enumFileCharChunks} or {@link #enumFileChars} - * as an alternative. - * - * @param f the file to read, must not be null - * @param encoding the encoding to use, {@link Option#none()} means platform default - * @param i the iteratee that is fed with lines read from the file - */ - 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)); - } - - /** - * An IO monad that reads char chunks from the given file and passes them to the given iteratee. - * - * @param f the file to read, must not be null - * @param encoding the encoding to use, {@link Option#none()} means platform default - * @param i the iteratee that is fed with char chunks read from the file - */ - 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)); - } - - /** - * An IO monad that reads char chunks from the given file and passes single chars to the given iteratee. - * - * @param f the file to read, must not be null - * @param encoding the encoding to use, {@link Option#none()} means platform default - * @param i the iteratee that is fed with chars read from the file - */ - 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)); - } - - public static IO bufferedReader(final File f, final Option encoding) { - return IOFunctions.map(fileReader(f, encoding), new F() { - @Override - public BufferedReader f(final Reader a) { - return new BufferedReader(a); - }}); - } - - public static IO fileReader(final File f, final Option encoding) { - return new IO() { - @Override - public Reader run() throws IOException { - final FileInputStream fis = new FileInputStream(f); - return encoding.isNone() ? new InputStreamReader(fis) : new InputStreamReader(fis, encoding.some()); - } - }; - } - - 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 final IO unit(final A a) { - return new IO() { - @Override - public A run() throws IOException { - return a; - } - }; - } - - public static final IO lazy(final P1 p) { - return () -> p._1(); - } - - public static final IO lazy(final F f) { + public static IO fromF(F0 p) { + return p::f; + } + + public static IO fromTry(Try0 t) { + return t::f; + } + + public static final F> closeReader = IOFunctions::closeReader; + + /** + * Convert io to a SafeIO, throwing any IOException wrapped inside a RuntimeException + * @param io + */ + public static SafeIO toSafe(IO io) { + return () -> { + try { + return io.run(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + } + + /** + * Run io, rethrowing any IOException wrapped in a RuntimeException + * @param io + */ + public static A runSafe(IO io) { + return toSafe(io).run(); + } + + public static IO closeReader(final Reader r) { + return () -> { + r.close(); + return Unit.unit(); + }; + } + + /** + * An IO monad that reads lines from the given file (using a {@link BufferedReader}) and passes + * lines to the provided iteratee. May not be suitable for files with very long + * lines, consider to use {@link #enumFileCharChunks} or {@link #enumFileChars} + * as an alternative. + * + * @param f the file to read, must not be null + * @param encoding the encoding to use, {@link Option#none()} means platform default + * @param i the iteratee that is fed with lines read from the file + */ + 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)); + } + + /** + * An IO monad that reads char chunks from the given file and passes them to the given iteratee. + * + * @param f the file to read, must not be null + * @param encoding the encoding to use, {@link Option#none()} means platform default + * @param i the iteratee that is fed with char chunks read from the file + */ + 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)); + } + + /** + * An IO monad that reads char chunks from the given file and passes single chars to the given iteratee. + * + * @param f the file to read, must not be null + * @param encoding the encoding to use, {@link Option#none()} means platform default + * @param i the iteratee that is fed with chars read from the file + */ + 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)); + } + + public static IO bufferedReader(final File f, final Option encoding) { + return map(fileReader(f, encoding), BufferedReader::new); + } + + public static IO fileReader(final File f, final Option encoding) { + return () -> { + final FileInputStream fis = new FileInputStream(f); + return encoding.isNone() ? new InputStreamReader(fis) : new InputStreamReader(fis, encoding.some()); + }; + } + + 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 IO unit(final A a) { + return () -> a; + } + + public static final IO ioUnit = unit(Unit.unit()); + + public static IO lazy(final F0 p) { + return fromF(p); + } + + 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 P1 f) { - return () -> f._1(); + public static SafeIO lazySafe(final F0 f) { + return f::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)); + * A function that feeds an iteratee with lines read from a {@link BufferedReader}. + */ + public static F, IO>>> lineReader() { + return LineReader::new; + } - @Override - public Boolean f(final IterV i) { - return i.fold(done, cont)._1(); - } - }; + private static class LineReader implements F, IO>> { + + private final BufferedReader r; - 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$ + 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$ - @Override - public IO> f(final IterV it) { + private LineReader(BufferedReader r) { + this.r = r; + } + + @Override + public IO> f(IterV it) { // use loop instead of recursion because of missing TCO - return new IO>() { - @Override - public IterV run() throws IOException { + return () -> { IterV i = it; while (!isDone.f(i)) { - final String s = r.readLine(); - if (s == null) { return i; } - final Input input = Input.el(s); - final F, IterV>, P1>> cont = F1Functions.lazy(Function., IterV>apply(input)); - i = i.fold(done, cont)._1(); + 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; - } }; - } - }; - } - }; - } - - /** - * A function that feeds an iteratee with character chunks read from a {@link Reader} - * (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(); } - }; + } + + /** + * A function that feeds an iteratee with character chunks read from a {@link Reader} + * (char[] of size {@link #DEFAULT_BUFFER_SIZE}). + */ + public static F, IO>>> charChunkReader() { + return CharChunkReader::new; + } - 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$ + private static class CharChunkReader implements F, IO>> { - @Override - public IO> f(final IterV it) { + 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 new IO>() { - @Override - public IterV run() throws IOException { - + return () -> { + IterV i = it; while (!isDone.f(i)) { - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - final int numRead = r.read(buffer); - if (numRead == -1) { return i; } - if(numRead < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, numRead); - } - final Input input = Input.el(buffer); - final F, IterV>, P1>> cont = - F1Functions.lazy(Function., IterV>apply(input)); - i = i.fold(done, cont)._1(); + 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 = + 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(); } - }; + } + + + + /** + * 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() { + return CharChunkReader2::new; + } + + private static class CharChunkReader2 implements F, IO>> { + + private final Reader r; - 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$ + 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(final IterV it) { + @Override + public IO> f(IterV it) { // use loop instead of recursion because of missing TCO - return new IO>() { - @Override - public IterV run() throws IOException { - + return () -> { + IterV i = it; while (!isDone.f(i)) { - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - final int numRead = r.read(buffer); - if (numRead == -1) { return i; } - if(numRead < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, numRead); - } - for(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); - } + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + final int numRead = r.read(buffer); + if (numRead == -1) { + return i; + } + if (numRead < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, numRead); + } + for (char c : buffer) { + final Input input = Input.el(c); + final F, IterV>, IterV> cont = + Function.apply(input); + i = i.fold(done, cont); + } } return i; - } }; - } + } + } + + public static IO map(final IO io, final F f) { + return () -> f.f(io.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); + } + + /** + * Evaluate each action in the sequence from left to right, and collect the results. + */ + public static IO> sequence(List> list) { + F2, IO>, IO>> f2 = (io, ioList) -> + 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) -> + bind(ioList, (xs) -> map(io, x -> Stream.cons(x, () -> xs))); + return stream.foldLeft(f2, unit(Stream.nil())); + } + + + public static IO join(IO> io1) { + return bind(io1, io2 -> io2); + } + + public static SafeIO> toSafeValidation(IO io) { + return () -> Try.f(io::run)._1(); + } + + public static IO append(final IO io1, final IO io2) { + return () -> { + io1.run(); + return io2.run(); }; - } - }; - } - - 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 final IO bind(final IO io, final F> f) { - return new IO() { - @Override - public B run() throws IOException { - return f.f(io.run()).run(); - } - }; - } - - /** - * Evaluate each action in the sequence from left to right, and collect the results. - */ - 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())); - } - - - public static IO> sequence(Stream> stream) { - F2>, IO, IO>> f2 = (ioList, io) -> - IOFunctions.bind(ioList, (xs) -> map(io, x -> Stream.cons(x, P.lazy(u -> xs)))); - return stream.foldLeft(f2, IOFunctions.unit(Stream.nil())); - } - - - /** - * Map each element of a structure to an action, evaluate these actions from left to right - * and collect the results. - */ - public static IO> traverse(List list, F> f) { - F2>, IO>> f2 = (a, acc) -> - bind(acc, (bs) -> map(f.f(a), b -> bs.append(List.list(b)))); - return list.foldRight(f2, IOFunctions.unit(List.nil())); - } - - public static IO join(IO> io1) { - return bind(io1, io2 -> io2); - } - - public static SafeIO> toSafeIO(IO io) { - return () -> Try.f(() -> io.run())._1(); - } - - public static IO append(final IO io1, final IO io2) { - return () -> { - io1.run(); - return io2.run(); - }; - } - - public static IO left(final IO io1, final IO io2) { - return () -> { - A a = io1.run(); - io2.run(); - return a; - }; - } - - public static IO flatMap(final IO io, final F> f) { - return bind(io, f); - } - - static IO> sequenceWhile(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()) { - loop = false; - } else { - A a = input.head().run(); - if (!f.f(a)) { - loop = false; - } else { - input = input.tail()._1(); - result = result.cons(a); - } - } - } - return result.reverse(); - } - }; - } - - public static IO apply(IO io, IO> iof) { - return bind(iof, f -> map(io, a -> f.f(a))); - } - - public static IO liftM2(IO ioa, IO iob, F2 f) { - return bind(ioa, a -> map(iob, b -> f.f(a, b))); - } - - public static IO> replicateM(IO ioa, int n) { - return sequence(List.replicate(n, ioa)); - } - - public static IO>> readerState() { - return () -> State.unit((BufferedReader r) -> P.p(r, Try.f((BufferedReader r2) -> r2.readLine()).f(r))); - } - - public static final BufferedReader stdinBufferedReader = new BufferedReader(new InputStreamReader(System.in)); - - public static IO stdinReadLine() { - return () -> stdinBufferedReader.readLine(); - } - - public static IO stdoutPrintln(final String s) { - return () -> { - System.out.println(s); - return Unit.unit(); - }; - } + } + + public static IO left(final IO io1, final IO io2) { + return () -> { + A a = io1.run(); + io2.run(); + return a; + }; + } + + public static IO flatMap(final IO io, final F> f) { + return bind(io, f); + } + + /** + * Read lines from stdin until condition is not met, transforming each line and printing + * the result to stdout. + * @param condition Read lines until a line does not satisfy condition + * @param transform Function to change line value + */ + public static IO interactWhile(F condition, F transform) { + 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 () -> { + 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 { + input = input.tail()._1(); + result = result.cons(a); + } + } + } + return result.reverse(); + }; + } + + public static IO> sequenceWhile(final Stream> stream, final F f) { + 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> 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, f)); + } + + public static IO liftM2(IO ioa, IO iob, F2 f) { + return bind(ioa, a -> map(iob, b -> f.f(a, b))); + } + + public static IO> replicateM(IO ioa, int n) { + return sequence(List.replicate(n, ioa)); + } + + public static IO>> readerState() { + 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; + } + + public static IO stdoutPrintln(final String s) { + return () -> { + System.out.println(s); + return Unit.unit(); + }; + } + + public static IO stdoutPrint(final String s) { + return () -> { + System.out.print(s); + return Unit.unit(); + }; + } + + public static IO getContents() { + 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) { + return bind(getContents(), ls1 -> { + LazyString ls2 = f.f(ls1); + return stdoutPrintln(ls2.toString()); + }); + } } diff --git a/core/src/main/java/fj/data/IterableW.java b/core/src/main/java/fj/data/IterableW.java index 5a3aa66e..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,11 +42,7 @@ public static IterableW wrap(final Iterable a) { * @return A function that returns the given iterable, wrapped. */ public static > F> wrap() { - return new F>() { - public IterableW f(final T a) { - return wrap(a); - } - }; + return IterableW::wrap; } /** @@ -69,11 +63,7 @@ public static IterableW iterable(final A a) { * @return The equivalent function whose return value is iterable. */ public static F> iterable(final F f) { - return new F>() { - public IterableW f(final A a) { - return iterable(f.f(a)); - } - }; + return a -> iterable(f.f(a)); } /** @@ -83,11 +73,7 @@ public IterableW f(final A a) { * @return A transformation from a function to the equivalent Iterable-valued function. */ public static F, F>> arrow() { - return new F, F>>() { - public F> f(final F f) { - return iterable(f); - } - }; + return IterableW::iterable; } /** @@ -97,11 +83,7 @@ public F> f(final F f) { * @return an iterable result of binding the given function over the wrapped Iterable. */ public > IterableW bind(final F f) { - return wrap(iterableStream(this).bind(new F>() { - public Stream f(final A a) { - return iterableStream(f.f(a)); - } - })); + return wrap(iterableStream(this).bind(a -> iterableStream(f.f(a)))); } /** @@ -111,11 +93,7 @@ public Stream f(final A a) { * @return A new iterable after applying the given iterable function to the wrapped iterable. */ public IterableW apply(final Iterable> f) { - return wrap(f).bind(new F, Iterable>() { - public Iterable f(final F f) { - return map(f); - } - }); + return wrap(f).bind(this::map); } /** @@ -137,11 +115,7 @@ public static IterableW bind(final Iterable a, final Iterable * @return A function of arity-2 promoted to map over iterables. */ public static F, F, IterableW>> liftM2(final F> f) { - return curry(new F2, Iterable, IterableW>() { - public IterableW f(final Iterable ca, final Iterable cb) { - return bind(ca, cb, f); - } - }); + return curry((ca, cb) -> bind(ca, cb, f)); } /** @@ -153,20 +127,13 @@ public IterableW f(final Iterable ca, final Iterable cb) { */ public static > IterableW> sequence(final Iterable as) { final Stream ts = iterableStream(as); - return ts.isEmpty() ? iterable(wrap(Option.none())) : wrap(ts.head()).bind(new F>>() { - public Iterable> f(final A a) { - return sequence(ts.tail().map(IterableW.>wrap())._1()) - .bind(new F, Iterable>>() { - public Iterable> f(final IterableW as) { - return iterable(wrap(Stream.cons(a, new P1>() { - public Stream _1() { - return iterableStream(as); - } - }))); - } - }); - } - }); + return ts.isEmpty() ? + iterable(wrap(Option.none())) : + wrap(ts.head()).bind(a -> + sequence(ts.tail().map(IterableW.wrap())._1()).bind(as2 -> + iterable(wrap(Stream.cons(a, () -> iterableStream(as2)))) + ) + ); } /** @@ -176,15 +143,7 @@ public Stream _1() { * @return a function that binds a given function across a given iterable. */ public static > F, F, IterableW>> bind() { - return new F, F, IterableW>>() { - public F, IterableW> f(final IterableW a) { - return new F, IterableW>() { - public IterableW f(final F f) { - return a.bind(f); - } - }; - } - }; + return a -> a::bind; } /** @@ -204,11 +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 new F, IterableW>() { - public IterableW f(final Iterable a) { - return join(a); - } - }; + return IterableW::join; } /** @@ -227,15 +182,7 @@ public IterableW map(final F f) { * @return a function that promotes any function so that it operates on Iterables. */ public static F, F, IterableW>> map() { - return new F, F, IterableW>>() { - public F, IterableW> f(final F f) { - return new F, IterableW>() { - public IterableW f(final IterableW a) { - return a.map(f); - } - }; - } - }; + return f -> a -> a.map(f); } /** @@ -284,11 +231,7 @@ public A foldLeft1(final F> f) { */ public B foldRight(final F2 f, final B z) { final F id = identity(); - return foldLeft(curry(new F3, A, B, B>() { - public B f(final F k, final A a, final B b) { - return k.f(f.f(a, b)); - } - }), id).f(z); + return foldLeft(curry((k, a, b) -> k.f(f.f(a, b))), id).f(z); } /** @@ -379,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)); } @@ -392,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); } @@ -475,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 7d3cc503..e6177068 100644 --- a/core/src/main/java/fj/data/Iteratee.java +++ b/core/src/main/java/fj/data/Iteratee.java @@ -1,58 +1,54 @@ package fj.data; import fj.F; -import fj.F1Functions; +import fj.F0; 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 P1 empty, final P1> el, final P1 eof); + 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 P1 empty, final P1> el, final P1 eof) { - return empty._1(); + public Z apply(final F0 empty, final F0> el, final F0 eof) { + return empty.f(); } }; } /** Input that is exhausted */ - public static final Input eof() { + public static Input eof() { return new Input() { @Override - public Z apply(final P1 empty, final P1> el, final P1 eof) { - return eof._1(); + public Z apply(final F0 empty, final F0> el, final F0 eof) { + return eof.f(); } }; } /** 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 P1 empty, final P1> el, final P1 eof) { - return el._1().f(element); + public Z apply(final F0 empty, final F0> el, final F0 eof) { + return el.f().f(element); } }; } } /** A pure iteratee computation which is either done or needs more input */ - public static abstract class IterV { + public abstract static class IterV { IterV() {} // sealed @@ -82,133 +78,71 @@ 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 = - new F, IterV>, A>() { - @Override - public A f(final F, IterV> k) { - return runCont.f(k.f(Input.eof())).valueE("diverging iteratee"); //$NON-NLS-1$ - } - }; + 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 = - new F>, IterV>() { - @Override - public IterV f(final P2> y_) { - final B y = y_._1(); - return done(y, e); - } - }; + final F>, IterV> done1 = + y_ -> { + final B y = y_._1(); + return done(y, e); + }; final F, IterV>, IterV> cont = - new F, IterV>, IterV>() { - @Override - public IterV f(final F, IterV> k) { - return k.f(e); - } - }; + 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 = - new F, IterV>, IterV>() { - @Override - public IterV f(final F, IterV> k) { - return cont(new F, IterV>() { - @Override - public IterV f(final Input e) { - return k.f(e).bind(f); - } - }); - } - }; + 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; @Override public F, IterV> f(final Integer acc) { - final P1> empty = - new P1>() { - @Override - public IterV _1() { - return cont(step.f(acc)); - } - }; - final P1>> el = - new P1>>() { - @Override - public F> _1() { - return P.p(cont(step.f(acc + 1))).constant(); - } - }; - final P1> eof = - new P1>() { - @Override - public IterV _1() { - return done(acc, Input.eof()); - } - }; - return new F, IterV>() { - @Override - public IterV f(final Input s) { - return s.apply(empty, el, eof); - } - }; + final F0> empty = () -> cont(step.f(acc)); + + final F0>> el = () -> P.p(cont(step.f(acc + 1))).constant(); + + final F0> eof = () -> done(acc, Input.eof()); + + return s -> s.apply(empty, el, eof); } }; return cont(step.f(0)); } /** 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; - final P1> empty = - new P1>() { - @Override - public IterV _1() { - return cont(step); - } - }; - final P1>> el = - new P1>>() { - @Override - public F> _1() { - return P.p(IterV.drop(n - 1)).constant(); - } - }; - final P1> eof = - new P1>() { - @Override - public IterV _1() { - return done(Unit.unit(), Input.eof()); - } - }; + final F0> empty = () -> cont(step); + + final F0>> el = () -> P.p(IterV.drop(n - 1)).constant(); + + final F0> eof = () -> done(Unit.unit(), Input.eof()); @Override public IterV f(final Input s) { @@ -216,42 +150,21 @@ 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 P1>> empty = - new P1>>() { - @Override - public IterV> _1() { - return cont(step); - } - }; - final P1>>> el = - new P1>>>() { - @Override - public F>> _1() { - return new F>>() { - @Override - public IterV> f(final E e) { - return done(Option.some(e), Input.empty()); - } - }; - } - }; - final P1>> eof = - new P1>>() { - @Override - public IterV> _1() { - return done(Option.none(), Input.eof()); - } - }; + final F0>> empty = () -> cont(step); + + final F0>>> el = () -> e -> done(Option.some(e), Input.empty()); + + final F0>> eof = () -> done(Option.none(), Input.eof()); @Override public IterV> f(final Input s) { @@ -262,37 +175,16 @@ 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 P1>> empty = - new P1>>() { - @Override - public IterV> _1() { - return cont(step); - } - }; - final P1>>> el = - new P1>>>() { - @Override - public F>> _1() { - return new F>>() { - @Override - public IterV> f(final E e) { - return done(Option.some(e), Input.el(e)); - } - }; - } - }; - final P1>> eof = - new P1>>() { - @Override - public IterV> _1() { - return done(Option.none(), Input.eof()); - } - }; + final F0>> empty = () -> cont(step); + + final F0>>> el = () -> e -> done(Option.some(e), Input.el(e)); + + final F0>> eof = () -> done(Option.none(), Input.eof()); @Override public IterV> f(final Input s) { @@ -304,48 +196,23 @@ 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; @Override public F, IterV>> f(final List acc) { - final P1>> empty = - new P1>>() { - @Override - public IterV> _1() { - return cont(step.f(acc)); - } - }; - final P1>>> el = - new P1>>>() { - @Override - public F>> _1() { - return new F>>() { - @Override - public IterV> f(final E e) { - return cont(step.f(acc.cons(e))); - } - }; - } - }; - final P1>> eof = - new P1>>() { - @Override - public IterV> _1() { - return done(acc, Input.eof()); - } - }; - return new F, IterV>>() { - @Override - public IterV> f(final Input s) { - return s.apply(empty, el, eof); - } - }; + final F0>> empty = () -> cont(step.f(acc)); + + final F0>>> el = () -> e -> cont(step.f(acc.cons(e))); + + final F0>> eof = () -> done(acc, Input.eof()); + + 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 b75c7c94..b3cb7e7f 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -1,14 +1,9 @@ package fj.data; -import fj.F; -import static fj.P.p; +import fj.*; -import fj.Function; -import fj.P1; -import fj.P2; -import fj.function.Effect1; +import static fj.P.p; -import static fj.data.List.list; import static fj.data.Option.some; import java.util.Arrays; @@ -40,8 +35,6 @@ /** * Functions that convert between types from the core Java API. - * - * @version %build.number% */ public final class Java { private Java() { @@ -56,26 +49,16 @@ private Java() { * @return A function that converts lists to array lists. */ public static F, ArrayList> List_ArrayList() { - return new F, ArrayList>() { - public ArrayList f(final List as) { - return new ArrayList(as.toCollection()); - } - }; + return as -> new ArrayList<>(as.toCollection()); } /** * A function that converts lists to bit sets. */ - public static final F, BitSet> List_BitSet = 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 F, BitSet> List_BitSet = bs -> { + final BitSet s = new BitSet(bs.length()); + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); + return s; }; /** @@ -84,11 +67,7 @@ public void f(final P2 bi) { * @return A function that converts lists to enum sets. */ public static > F, EnumSet> List_EnumSet() { - return new F, EnumSet>() { - public EnumSet f(final List as) { - return copyOf(as.toCollection()); - } - }; + return as -> copyOf(as.toCollection()); } /** @@ -97,11 +76,7 @@ public EnumSet f(final List as) { * @return A function that converts lists to hash sets. */ public static F, HashSet> List_HashSet() { - return new F, HashSet>() { - public HashSet f(final List as) { - return new HashSet(as.toCollection()); - } - }; + return as -> new HashSet<>(as.toCollection()); } /** @@ -110,11 +85,7 @@ public HashSet f(final List as) { * @return A function that converts lists to linked hash sets. */ public static F, LinkedHashSet> List_LinkedHashSet() { - return new F, LinkedHashSet>() { - public LinkedHashSet f(final List as) { - return new LinkedHashSet(as.toCollection()); - } - }; + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -123,11 +94,7 @@ public LinkedHashSet f(final List as) { * @return A function that converts lists to linked lists. */ public static F, LinkedList> List_LinkedList() { - return new F, LinkedList>() { - public LinkedList f(final List as) { - return new LinkedList(as.toCollection()); - } - }; + return as -> new LinkedList<>(as.toCollection()); } /** @@ -136,11 +103,7 @@ public LinkedList f(final List as) { * @return A function that converts lists to priority queues. */ public static F, PriorityQueue> List_PriorityQueue() { - return new F, PriorityQueue>() { - public PriorityQueue f(final List as) { - return new PriorityQueue(as.toCollection()); - } - }; + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -149,12 +112,10 @@ public PriorityQueue f(final List as) { * @return A function that converts lists to stacks. */ public static F, Stack> List_Stack() { - return new F, Stack>() { - public Stack f(final List as) { - final Stack s = new Stack(); - s.addAll(as.toCollection()); - return s; - } + return as -> { + final Stack s = new Stack<>(); + s.addAll(as.toCollection()); + return s; }; } @@ -164,11 +125,7 @@ public Stack f(final List as) { * @return A function that converts lists to stacks. */ public static F, TreeSet> List_TreeSet() { - return new F, TreeSet>() { - public TreeSet f(final List as) { - return new TreeSet(as.toCollection()); - } - }; + return as -> new TreeSet<>(as.toCollection()); } /** @@ -177,12 +134,7 @@ public TreeSet f(final List as) { * @return A function that converts lists to vectors. */ public static F, Vector> List_Vector() { - return new F, Vector>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Vector f(final List as) { - return new Vector(as.toCollection()); - } - }; + return as -> new Vector<>(as.toCollection()); } /** @@ -192,11 +144,7 @@ public Vector f(final List as) { * @return A function that converts lists to array blocking queue. */ public static F, ArrayBlockingQueue> List_ArrayBlockingQueue(final boolean fair) { - return new F, ArrayBlockingQueue>() { - public ArrayBlockingQueue f(final List as) { - return new ArrayBlockingQueue(as.length(), fair, as.toCollection()); - } - }; + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -205,11 +153,7 @@ public ArrayBlockingQueue f(final List as) { * @return A function that converts lists to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> List_ConcurrentLinkedQueue() { - return new F, ConcurrentLinkedQueue>() { - public ConcurrentLinkedQueue f(final List as) { - return new ConcurrentLinkedQueue(as.toCollection()); - } - }; + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -218,11 +162,7 @@ public ConcurrentLinkedQueue f(final List as) { * @return A function that converts lists to copy on write array lists. */ public static F, CopyOnWriteArrayList> List_CopyOnWriteArrayList() { - return new F, CopyOnWriteArrayList>() { - public CopyOnWriteArrayList f(final List as) { - return new CopyOnWriteArrayList(as.toCollection()); - } - }; + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -231,11 +171,7 @@ public CopyOnWriteArrayList f(final List as) { * @return A function that converts lists to copy on write array sets. */ public static F, CopyOnWriteArraySet> List_CopyOnWriteArraySet() { - return new F, CopyOnWriteArraySet>() { - public CopyOnWriteArraySet f(final List as) { - return new CopyOnWriteArraySet(as.toCollection()); - } - }; + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -244,11 +180,7 @@ public CopyOnWriteArraySet f(final List as) { * @return A function that converts lists to delay queues. */ public static F, DelayQueue> List_DelayQueue() { - return new F, DelayQueue>() { - public DelayQueue f(final List as) { - return new DelayQueue(as.toCollection()); - } - }; + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -257,11 +189,7 @@ public DelayQueue f(final List as) { * @return A function that converts lists to linked blocking queues. */ public static F, LinkedBlockingQueue> List_LinkedBlockingQueue() { - return new F, LinkedBlockingQueue>() { - public LinkedBlockingQueue f(final List as) { - return new LinkedBlockingQueue(as.toCollection()); - } - }; + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -270,11 +198,7 @@ public LinkedBlockingQueue f(final List as) { * @return A function that converts lists to priority blocking queues. */ public static F, PriorityBlockingQueue> List_PriorityBlockingQueue() { - return new F, PriorityBlockingQueue>() { - public PriorityBlockingQueue f(final List as) { - return new PriorityBlockingQueue(as.toCollection()); - } - }; + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -284,12 +208,10 @@ public PriorityBlockingQueue f(final List as) { * @return A function that converts lists to synchronous queues. */ public static F, SynchronousQueue> List_SynchronousQueue(final boolean fair) { - return new F, SynchronousQueue>() { - public SynchronousQueue f(final List as) { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(as.toCollection()); - return q; - } + return as -> { + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(as.toCollection()); + return q; }; } @@ -303,27 +225,17 @@ public SynchronousQueue f(final List as) { * @return A function that converts arrays to array lists. */ public static F, ArrayList> Array_ArrayList() { - return new F, ArrayList>() { - public ArrayList f(final Array as) { - return new ArrayList(as.toCollection()); - } - }; + return Array::toJavaList; } /** * A function that converts arrays to bit sets. */ - public static final F, BitSet> Array_BitSet = new F, BitSet>() { - public BitSet f(final Array bs) { - final BitSet s = new BitSet(bs.length()); + public static final F, BitSet> Array_BitSet = bs -> { + final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreach(new Effect1>() { - public void f(final P2 bi) { - s.set(bi._2(), bi._1()); - } - }); - return s; - } + bs.zipIndex().foreachDoEffect(bi -> s.set(bi._2(), bi._1())); + return s; }; /** @@ -332,11 +244,7 @@ public void f(final P2 bi) { * @return A function that converts arrays to enum sets. */ public static > F, EnumSet> Array_EnumSet() { - return new F, EnumSet>() { - public EnumSet f(final Array as) { - return copyOf(as.toCollection()); - } - }; + return as -> copyOf(as.asJavaList()); } /** @@ -345,11 +253,7 @@ public EnumSet f(final Array as) { * @return A function that converts arrays to hash sets. */ public static F, HashSet> Array_HashSet() { - return new F, HashSet>() { - public HashSet f(final Array as) { - return new HashSet(as.toCollection()); - } - }; + return as -> new HashSet<>(as.asJavaList()); } /** @@ -358,11 +262,7 @@ public HashSet f(final Array as) { * @return A function that converts arrays to linked hash sets. */ public static F, LinkedHashSet> Array_LinkedHashSet() { - return new F, LinkedHashSet>() { - public LinkedHashSet f(final Array as) { - return new LinkedHashSet(as.toCollection()); - } - }; + return as -> new LinkedHashSet<>(as.asJavaList()); } /** @@ -371,11 +271,7 @@ public LinkedHashSet f(final Array as) { * @return A function that converts arrays to linked lists. */ public static F, LinkedList> Array_LinkedList() { - return new F, LinkedList>() { - public LinkedList f(final Array as) { - return new LinkedList(as.toCollection()); - } - }; + return as -> new LinkedList<>(as.asJavaList()); } /** @@ -384,11 +280,7 @@ public LinkedList f(final Array as) { * @return A function that converts arrays to priority queues. */ public static F, PriorityQueue> Array_PriorityQueue() { - return new F, PriorityQueue>() { - public PriorityQueue f(final Array as) { - return new PriorityQueue(as.toCollection()); - } - }; + return as -> new PriorityQueue<>(as.asJavaList()); } /** @@ -397,12 +289,10 @@ public PriorityQueue f(final Array as) { * @return A function that converts arrays to stacks. */ public static F, Stack> Array_Stack() { - return new F, Stack>() { - public Stack f(final Array as) { - final Stack s = new Stack(); - s.addAll(as.toCollection()); - return s; - } + return as -> { + final Stack s = new Stack<>(); + s.addAll(as.asJavaList()); + return s; }; } @@ -412,11 +302,7 @@ public Stack f(final Array as) { * @return A function that converts arrays to tree sets. */ public static F, TreeSet> Array_TreeSet() { - return new F, TreeSet>() { - public TreeSet f(final Array as) { - return new TreeSet(as.toCollection()); - } - }; + return as -> new TreeSet<>(as.asJavaList()); } /** @@ -425,12 +311,7 @@ public TreeSet f(final Array as) { * @return A function that converts arrays to vectors. */ public static F, Vector> Array_Vector() { - return new F, Vector>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Vector f(final Array as) { - return new Vector(as.toCollection()); - } - }; + return as -> new Vector<>(as.asJavaList()); } /** @@ -440,11 +321,7 @@ public Vector f(final Array as) { * @return A function that converts arrays to array blocking queues. */ public static F, ArrayBlockingQueue> Array_ArrayBlockingQueue(final boolean fair) { - return new F, ArrayBlockingQueue>() { - public ArrayBlockingQueue f(final Array as) { - return new ArrayBlockingQueue(as.length(), fair, as.toCollection()); - } - }; + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.asJavaList()); } /** @@ -453,11 +330,7 @@ public ArrayBlockingQueue f(final Array as) { * @return A function that converts arrays to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Array_ConcurrentLinkedQueue() { - return new F, ConcurrentLinkedQueue>() { - public ConcurrentLinkedQueue f(final Array as) { - return new ConcurrentLinkedQueue(as.toCollection()); - } - }; + return as -> new ConcurrentLinkedQueue<>(as.asJavaList()); } /** @@ -466,11 +339,7 @@ public ConcurrentLinkedQueue f(final Array as) { * @return A function that converts arrays to copy on write array lists. */ public static F, CopyOnWriteArrayList> Array_CopyOnWriteArrayList() { - return new F, CopyOnWriteArrayList>() { - public CopyOnWriteArrayList f(final Array as) { - return new CopyOnWriteArrayList(as.toCollection()); - } - }; + return as -> new CopyOnWriteArrayList<>(as.asJavaList()); } /** @@ -479,11 +348,7 @@ public CopyOnWriteArrayList f(final Array as) { * @return A function that converts arrays to copy on write array sets. */ public static F, CopyOnWriteArraySet> Array_CopyOnWriteArraySet() { - return new F, CopyOnWriteArraySet>() { - public CopyOnWriteArraySet f(final Array as) { - return new CopyOnWriteArraySet(as.toCollection()); - } - }; + return as -> new CopyOnWriteArraySet<>(as.asJavaList()); } /** @@ -492,11 +357,7 @@ public CopyOnWriteArraySet f(final Array as) { * @return A function that converts arrays to delay queues. */ public static F, DelayQueue> Array_DelayQueue() { - return new F, DelayQueue>() { - public DelayQueue f(final Array as) { - return new DelayQueue(as.toCollection()); - } - }; + return as -> new DelayQueue<>(as.asJavaList()); } /** @@ -505,11 +366,7 @@ public DelayQueue f(final Array as) { * @return A function that converts arrays to linked blocking queues. */ public static F, LinkedBlockingQueue> Array_LinkedBlockingQueue() { - return new F, LinkedBlockingQueue>() { - public LinkedBlockingQueue f(final Array as) { - return new LinkedBlockingQueue(as.toCollection()); - } - }; + return as -> new LinkedBlockingQueue<>(as.asJavaList()); } /** @@ -518,11 +375,7 @@ public LinkedBlockingQueue f(final Array as) { * @return A function that converts arrays to priority blocking queues. */ public static F, PriorityBlockingQueue> Array_PriorityBlockingQueue() { - return new F, PriorityBlockingQueue>() { - public PriorityBlockingQueue f(final Array as) { - return new PriorityBlockingQueue(as.toCollection()); - } - }; + return as -> new PriorityBlockingQueue<>(as.asJavaList()); } /** @@ -532,12 +385,10 @@ public PriorityBlockingQueue f(final Array as) { * @return A function that converts arrays to synchronous queues. */ public static F, SynchronousQueue> Array_SynchronousQueue(final boolean fair) { - return new F, SynchronousQueue>() { - public SynchronousQueue f(final Array as) { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(as.toCollection()); - return q; - } + return as -> { + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(as.asJavaList()); + return q; }; } @@ -551,35 +402,7 @@ public SynchronousQueue f(final Array as) { * @return A function that converts streams to iterable. */ public static F, Iterable> Stream_Iterable() { - return new F, Iterable>() { - public Iterable f(final Stream as) { - return new Iterable() { - public Iterator iterator() { - return 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; } /** @@ -588,26 +411,16 @@ public void remove() { * @return A function that converts streams to array lists. */ public static F, ArrayList> Stream_ArrayList() { - return new F, ArrayList>() { - public ArrayList f(final Stream as) { - return new ArrayList(as.toCollection()); - } - }; + return as -> new ArrayList<>(as.toCollection()); } /** * A function that converts streams to bit sets. */ - public static final F, BitSet> Stream_BitSet = new F, BitSet>() { - public BitSet f(final Stream bs) { - final BitSet s = new BitSet(bs.length()); - bs.zipIndex().foreach(new Effect1>() { - public void f(final P2 bi) { - s.set(bi._2(), bi._1()); - } - }); - return s; - } + 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())); + return s; }; /** @@ -616,11 +429,7 @@ public void f(final P2 bi) { * @return A function that converts streams to enum sets. */ public static > F, EnumSet> Stream_EnumSet() { - return new F, EnumSet>() { - public EnumSet f(final Stream as) { - return copyOf(as.toCollection()); - } - }; + return as -> copyOf(as.toCollection()); } /** @@ -629,11 +438,7 @@ public EnumSet f(final Stream as) { * @return A function that converts streams to hash sets. */ public static F, HashSet> Stream_HashSet() { - return new F, HashSet>() { - public HashSet f(final Stream as) { - return new HashSet(as.toCollection()); - } - }; + return as -> new HashSet<>(as.toCollection()); } /** @@ -642,11 +447,7 @@ public HashSet f(final Stream as) { * @return A function that converts streams to linked hash sets. */ public static F, LinkedHashSet> Stream_LinkedHashSet() { - return new F, LinkedHashSet>() { - public LinkedHashSet f(final Stream as) { - return new LinkedHashSet(as.toCollection()); - } - }; + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -655,11 +456,7 @@ public LinkedHashSet f(final Stream as) { * @return A function that converts streams to linked lists. */ public static F, LinkedList> Stream_LinkedList() { - return new F, LinkedList>() { - public LinkedList f(final Stream as) { - return new LinkedList(as.toCollection()); - } - }; + return as -> new LinkedList<>(as.toCollection()); } /** @@ -668,11 +465,7 @@ public LinkedList f(final Stream as) { * @return A function that converts streams to priority queues. */ public static F, PriorityQueue> Stream_PriorityQueue() { - return new F, PriorityQueue>() { - public PriorityQueue f(final Stream as) { - return new PriorityQueue(as.toCollection()); - } - }; + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -681,12 +474,10 @@ public PriorityQueue f(final Stream as) { * @return A function that converts streams to stacks. */ public static F, Stack> Stream_Stack() { - return new F, Stack>() { - public Stack f(final Stream as) { - final Stack s = new Stack(); - s.addAll(as.toCollection()); - return s; - } + return as -> { + final Stack s = new Stack<>(); + s.addAll(as.toCollection()); + return s; }; } @@ -696,11 +487,7 @@ public Stack f(final Stream as) { * @return A function that converts streams to tree sets. */ public static F, TreeSet> Stream_TreeSet() { - return new F, TreeSet>() { - public TreeSet f(final Stream as) { - return new TreeSet(as.toCollection()); - } - }; + return as -> new TreeSet<>(as.toCollection()); } /** @@ -709,12 +496,7 @@ public TreeSet f(final Stream as) { * @return A function that converts streams to vectors. */ public static F, Vector> Stream_Vector() { - return new F, Vector>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Vector f(final Stream as) { - return new Vector(as.toCollection()); - } - }; + return as -> new Vector<>(as.toCollection()); } /** @@ -724,11 +506,7 @@ public Vector f(final Stream as) { * @return A function that converts streams to array blocking queues. */ public static F, ArrayBlockingQueue> Stream_ArrayBlockingQueue(final boolean fair) { - return new F, ArrayBlockingQueue>() { - public ArrayBlockingQueue f(final Stream as) { - return new ArrayBlockingQueue(as.length(), fair, as.toCollection()); - } - }; + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -737,11 +515,7 @@ public ArrayBlockingQueue f(final Stream as) { * @return A function that converts streams to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Stream_ConcurrentLinkedQueue() { - return new F, ConcurrentLinkedQueue>() { - public ConcurrentLinkedQueue f(final Stream as) { - return new ConcurrentLinkedQueue(as.toCollection()); - } - }; + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -750,11 +524,7 @@ public ConcurrentLinkedQueue f(final Stream as) { * @return A function that converts streams to copy on write array lists. */ public static F, CopyOnWriteArrayList> Stream_CopyOnWriteArrayList() { - return new F, CopyOnWriteArrayList>() { - public CopyOnWriteArrayList f(final Stream as) { - return new CopyOnWriteArrayList(as.toCollection()); - } - }; + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -763,11 +533,7 @@ public CopyOnWriteArrayList f(final Stream as) { * @return A function that converts streams to copy on write array sets. */ public static F, CopyOnWriteArraySet> Stream_CopyOnWriteArraySet() { - return new F, CopyOnWriteArraySet>() { - public CopyOnWriteArraySet f(final Stream as) { - return new CopyOnWriteArraySet(as.toCollection()); - } - }; + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -776,11 +542,7 @@ public CopyOnWriteArraySet f(final Stream as) { * @return A function that converts streams to delay queues. */ public static F, DelayQueue> Stream_DelayQueue() { - return new F, DelayQueue>() { - public DelayQueue f(final Stream as) { - return new DelayQueue(as.toCollection()); - } - }; + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -789,11 +551,7 @@ public DelayQueue f(final Stream as) { * @return A function that converts streams to linked blocking queues. */ public static F, LinkedBlockingQueue> Stream_LinkedBlockingQueue() { - return new F, LinkedBlockingQueue>() { - public LinkedBlockingQueue f(final Stream as) { - return new LinkedBlockingQueue(as.toCollection()); - } - }; + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -802,11 +560,7 @@ public LinkedBlockingQueue f(final Stream as) { * @return A function that converts streams to priority blocking queues. */ public static F, PriorityBlockingQueue> Stream_PriorityBlockingQueue() { - return new F, PriorityBlockingQueue>() { - public PriorityBlockingQueue f(final Stream as) { - return new PriorityBlockingQueue(as.toCollection()); - } - }; + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -816,12 +570,10 @@ public PriorityBlockingQueue f(final Stream as) { * @return A function that converts streams to synchronous queues. */ public static F, SynchronousQueue> Stream_SynchronousQueue(final boolean fair) { - return new F, SynchronousQueue>() { - public SynchronousQueue f(final Stream as) { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(as.toCollection()); - return q; - } + return as -> { + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(as.toCollection()); + return q; }; } @@ -835,28 +587,20 @@ public SynchronousQueue f(final Stream as) { * @return A function that converts options to array lists. */ public static F, ArrayList> Option_ArrayList() { - return new F, ArrayList>() { - public ArrayList f(final Option as) { - return new ArrayList(as.toCollection()); - } - }; + return as -> new ArrayList<>(as.toCollection()); } /** * A function that converts options to bit sets. */ - public static final F, BitSet> Option_BitSet = new F, BitSet>() { - public BitSet f(final Option bs) { - final BitSet s = new BitSet(bs.length()); + public static final F, BitSet> Option_BitSet = bs -> { + final BitSet s = new BitSet(bs.length()); - bs.foreach(new Effect1() { - public void f(final Boolean b) { - if (b) - s.set(0); - } - }); - return s; - } + bs.foreachDoEffect(b -> { + if (b) + s.set(0); + }); + return s; }; /** @@ -865,11 +609,7 @@ public void f(final Boolean b) { * @return A function that converts options to enum sets. */ public static > F, EnumSet> Option_EnumSet() { - return new F, EnumSet>() { - public EnumSet f(final Option as) { - return copyOf(as.toCollection()); - } - }; + return as -> copyOf(as.toCollection()); } /** @@ -878,11 +618,7 @@ public EnumSet f(final Option as) { * @return A function that converts options to hash sets. */ public static F, HashSet> Option_HashSet() { - return new F, HashSet>() { - public HashSet f(final Option as) { - return new HashSet(as.toCollection()); - } - }; + return as -> new HashSet<>(as.toCollection()); } /** @@ -891,11 +627,7 @@ public HashSet f(final Option as) { * @return A function that converts options to linked hash sets. */ public static F, LinkedHashSet> Option_LinkedHashSet() { - return new F, LinkedHashSet>() { - public LinkedHashSet f(final Option as) { - return new LinkedHashSet(as.toCollection()); - } - }; + return as -> new LinkedHashSet<>(as.toCollection()); } /** @@ -904,11 +636,7 @@ public LinkedHashSet f(final Option as) { * @return A function that converts options to linked lists. */ public static F, LinkedList> Option_LinkedList() { - return new F, LinkedList>() { - public LinkedList f(final Option as) { - return new LinkedList(as.toCollection()); - } - }; + return as -> new LinkedList<>(as.toCollection()); } /** @@ -917,11 +645,7 @@ public LinkedList f(final Option as) { * @return A function that converts options to priority queues. */ public static F, PriorityQueue> Option_PriorityQueue() { - return new F, PriorityQueue>() { - public PriorityQueue f(final Option as) { - return new PriorityQueue(as.toCollection()); - } - }; + return as -> new PriorityQueue<>(as.toCollection()); } /** @@ -930,12 +654,10 @@ public PriorityQueue f(final Option as) { * @return A function that converts options to stacks. */ public static F, Stack> Option_Stack() { - return new F, Stack>() { - public Stack f(final Option as) { - final Stack s = new Stack(); - s.addAll(as.toCollection()); - return s; - } + return as -> { + final Stack s = new Stack<>(); + s.addAll(as.toCollection()); + return s; }; } @@ -945,11 +667,7 @@ public Stack f(final Option as) { * @return A function that converts options to tree sets. */ public static F, TreeSet> Option_TreeSet() { - return new F, TreeSet>() { - public TreeSet f(final Option as) { - return new TreeSet(as.toCollection()); - } - }; + return as -> new TreeSet<>(as.toCollection()); } /** @@ -958,12 +676,7 @@ public TreeSet f(final Option as) { * @return A function that converts options to vectors. */ public static F, Vector> Option_Vector() { - return new F, Vector>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Vector f(final Option as) { - return new Vector(as.toCollection()); - } - }; + return as -> new Vector<>(as.toCollection()); } /** @@ -973,11 +686,7 @@ public Vector f(final Option as) { * @return A function that converts options to array blocking queues. */ public static F, ArrayBlockingQueue> Option_ArrayBlockingQueue(final boolean fair) { - return new F, ArrayBlockingQueue>() { - public ArrayBlockingQueue f(final Option as) { - return new ArrayBlockingQueue(as.length(), fair, as.toCollection()); - } - }; + return as -> new ArrayBlockingQueue<>(as.length(), fair, as.toCollection()); } /** @@ -986,11 +695,7 @@ public ArrayBlockingQueue f(final Option as) { * @return A function that converts options to concurrent linked queues. */ public static F, ConcurrentLinkedQueue> Option_ConcurrentLinkedQueue() { - return new F, ConcurrentLinkedQueue>() { - public ConcurrentLinkedQueue f(final Option as) { - return new ConcurrentLinkedQueue(as.toCollection()); - } - }; + return as -> new ConcurrentLinkedQueue<>(as.toCollection()); } /** @@ -999,11 +704,7 @@ public ConcurrentLinkedQueue f(final Option as) { * @return A function that converts options to copy on write array lists. */ public static F, CopyOnWriteArrayList> Option_CopyOnWriteArrayList() { - return new F, CopyOnWriteArrayList>() { - public CopyOnWriteArrayList f(final Option as) { - return new CopyOnWriteArrayList(as.toCollection()); - } - }; + return as -> new CopyOnWriteArrayList<>(as.toCollection()); } /** @@ -1012,11 +713,7 @@ public CopyOnWriteArrayList f(final Option as) { * @return A function that converts options to copy on write array sets. */ public static F, CopyOnWriteArraySet> Option_CopyOnWriteArraySet() { - return new F, CopyOnWriteArraySet>() { - public CopyOnWriteArraySet f(final Option as) { - return new CopyOnWriteArraySet(as.toCollection()); - } - }; + return as -> new CopyOnWriteArraySet<>(as.toCollection()); } /** @@ -1025,11 +722,7 @@ public CopyOnWriteArraySet f(final Option as) { * @return A function that converts options to delay queues. */ public static F, DelayQueue> Option_DelayQueue() { - return new F, DelayQueue>() { - public DelayQueue f(final Option as) { - return new DelayQueue(as.toCollection()); - } - }; + return as -> new DelayQueue<>(as.toCollection()); } /** @@ -1038,11 +731,7 @@ public DelayQueue f(final Option as) { * @return A function that converts options to linked blocking queues. */ public static F, LinkedBlockingQueue> Option_LinkedBlockingQueue() { - return new F, LinkedBlockingQueue>() { - public LinkedBlockingQueue f(final Option as) { - return new LinkedBlockingQueue(as.toCollection()); - } - }; + return as -> new LinkedBlockingQueue<>(as.toCollection()); } /** @@ -1051,11 +740,7 @@ public LinkedBlockingQueue f(final Option as) { * @return A function that converts options to priority blocking queues. */ public static F, PriorityBlockingQueue> Option_PriorityBlockingQueue() { - return new F, PriorityBlockingQueue>() { - public PriorityBlockingQueue f(final Option as) { - return new PriorityBlockingQueue(as.toCollection()); - } - }; + return as -> new PriorityBlockingQueue<>(as.toCollection()); } /** @@ -1065,12 +750,10 @@ public PriorityBlockingQueue f(final Option as) { * @return A function that converts options to synchronous queues. */ public static F, SynchronousQueue> Option_SynchronousQueue(final boolean fair) { - return new F, SynchronousQueue>() { - public SynchronousQueue f(final Option as) { - final SynchronousQueue q = new SynchronousQueue(fair); - q.addAll(as.toCollection()); - return q; - } + return as -> { + final SynchronousQueue q = new SynchronousQueue<>(fair); + q.addAll(as.toCollection()); + return q; }; } @@ -1084,7 +767,7 @@ public SynchronousQueue f(final Option as) { * @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()); } /** @@ -1093,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()); } /** @@ -1102,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()); } /** @@ -1111,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()); } /** @@ -1138,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()); } /** @@ -1147,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()); } /** @@ -1156,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()); } /** @@ -1165,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()); } /** @@ -1174,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()); } /** @@ -1183,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()); } /** @@ -1192,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()); } /** @@ -1201,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()); } /** @@ -1210,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()); } /** @@ -1219,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()); } /** @@ -1228,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()); } /** @@ -1237,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()); } /** @@ -1246,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()); } /** @@ -1255,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()); } /** @@ -1265,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()); } /** @@ -1275,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()); } /** @@ -1284,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()); } /** @@ -1293,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()); } /** @@ -1302,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()); } /** @@ -1311,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()); } /** @@ -1320,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()); } /** @@ -1329,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()); } /** @@ -1338,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()); } /** @@ -1347,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()); } /** @@ -1356,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()); } /** @@ -1365,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()); } /** @@ -1374,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()); } /** @@ -1383,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()); } /** @@ -1393,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()); } /** @@ -1403,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 -> @@ -1414,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. @@ -1465,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. @@ -1505,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 -> @@ -1516,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. @@ -1567,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. @@ -1607,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 -> @@ -1618,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. @@ -1669,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. @@ -1709,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 -> @@ -1722,48 +1405,29 @@ public static F> StringBuilder_Synchr * @return A function that converts array lists to lists. */ public static F, List> ArrayList_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final ArrayList as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo // END ArrayList -> - /** - * A function that converts Java lists to lists. - * - * @return A function that converts Java lists to lists. - */ - public static F, List> JUList_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final java.util.List as) { - return Collection_List(as); - } - }; + public static F, List> JavaList_List() { + return Java::JavaList_List; + } + + public static List JavaList_List(java.util.List list) { + return List.iterableList(list); } - + // BEGIN BitSet -> /** * A function that converts bit sets to lists. */ - public static final F> BitSet_List = new F>() { - public List f(final BitSet s) { - return List.unfold(new F>>() { - public Option> f(final Integer i) { - return i == s.length() ? - Option.>none() : - some(p(s.get(i), i + 1)); - } - }, 0); - } - }; + public static final F> BitSet_List = s -> List.unfold(i -> i == s.length() ? + Option.none() : + some(p(s.get(i), i + 1)), 0); // todo @@ -1777,12 +1441,7 @@ public Option> f(final Integer i) { * @return A function that converts enum sets to lists. */ public static > F, List> EnumSet_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final EnumSet as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } public static List Collection_List(Collection c) { @@ -1790,7 +1449,7 @@ public static List Collection_List(Collection c) { } public static F, List> Collection_List() { - return c -> list(c.toArray(array(c.size()))); + return c -> List.list(c.toArray(array(c.size()))); } @SafeVarargs @@ -1810,12 +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 new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final HashSet as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1830,12 +1484,7 @@ public List f(final HashSet as) { * @return A function that converts linked hash sets to lists. */ public static F, List> LinkedHashSet_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final LinkedHashSet as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1850,12 +1499,7 @@ public List f(final LinkedHashSet as) { * @return A function that converts linked lists to lists. */ public static F, List> LinkedList_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final LinkedList as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1870,12 +1514,7 @@ public List f(final LinkedList as) { * @return A function that converts priority queues to lists. */ public static F, List> PriorityQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final PriorityQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1890,12 +1529,7 @@ public List f(final PriorityQueue as) { * @return A function that converts stacks to lists. */ public static F, List> Stack_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final Stack as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1910,12 +1544,7 @@ public List f(final Stack as) { * @return A function that converts tree sets to lists. */ public static F, List> TreeSet_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final TreeSet as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1930,12 +1559,7 @@ public List f(final TreeSet as) { * @return A function that converts vectors to lists. */ public static F, List> Vector_List() { - return new F, List>() { - @SuppressWarnings({"unchecked", "UseOfObsoleteCollectionType"}) - public List f(final Vector as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1950,12 +1574,7 @@ public List f(final Vector as) { * @return A function that converts array blocking queues to lists. */ public static F, List> ArrayBlockingQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final ArrayBlockingQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1970,12 +1589,7 @@ public List f(final ArrayBlockingQueue as) { * @return A function that converts concurrent linked queues to lists. */ public static F, List> ConcurrentLinkedQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final ConcurrentLinkedQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -1990,12 +1604,7 @@ public List f(final ConcurrentLinkedQueue as) { * @return A function that converts copy on write array lists to lists. */ public static F, List> CopyOnWriteArrayList_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final CopyOnWriteArrayList as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2010,12 +1619,7 @@ public List f(final CopyOnWriteArrayList as) { * @return A function that converts copy on write array sets to lists. */ public static F, List> CopyOnWriteArraySet_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final CopyOnWriteArraySet as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2030,12 +1634,7 @@ public List f(final CopyOnWriteArraySet as) { * @return A function that converts delay queues to lists. */ public static F, List> DelayQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final DelayQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2050,12 +1649,7 @@ public List f(final DelayQueue as) { * @return A function that converts linked blocking queues to lists. */ public static F, List> LinkedBlockingQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final LinkedBlockingQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2070,12 +1664,7 @@ public List f(final LinkedBlockingQueue as) { * @return A function that converts priority blocking queues to lists. */ public static F, List> PriorityBlockingQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final PriorityBlockingQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2090,12 +1679,7 @@ public List f(final PriorityBlockingQueue as) { * @return A function that converts synchronous queues to lists. */ public static F, List> SynchronousQueue_List() { - return new F, List>() { - @SuppressWarnings({"unchecked"}) - public List f(final SynchronousQueue as) { - return Collection_List(as); - } - }; + return Java::Collection_List; } // todo @@ -2105,15 +1689,7 @@ public List f(final SynchronousQueue as) { // BEGIN Callable -> public static F, Callable> P1_Callable() { - return new F, Callable>() { - public Callable f(final P1 a) { - return new Callable() { - public A call() { - return a._1(); - } - }; - } - }; + return a -> a::_1; } // END Callable -> @@ -2121,23 +1697,16 @@ public A call() { // BEGIN Future -> public static F, P1>> Future_P1() { - return new F, P1>>() { - public P1> f(final Future a) { - return new P1>() { - @SuppressWarnings({"OverlyBroadCatchBlock"}) - public Either _1() { - Either r; - try { - r = Either.right(a.get()); - } - catch (Exception e) { - r = Either.left(e); - } - return r; - } - }; - } - }; + return a -> P.lazy(() -> { + Either r; + try { + r = Either.right(a.get()); + } + catch (Exception e) { + r = Either.left(e); + } + return r; + }); } // END Future -> diff --git a/core/src/main/java/fj/data/Java8.java b/core/src/main/java/fj/data/Java8.java new file mode 100644 index 00000000..184c01c5 --- /dev/null +++ b/core/src/main/java/fj/data/Java8.java @@ -0,0 +1,162 @@ +package fj.data; + +import fj.F; +import fj.F2; +import fj.P; +import fj.P1; +import fj.Try; +import fj.Unit; +import fj.function.Try0; +import fj.function.Try1; +import fj.function.Try2; + +import java.util.Iterator; +import java.util.Optional; +import java.util.Spliterators; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.StreamSupport; + +public final class Java8 { + + private Java8() { + throw new UnsupportedOperationException(); + } + + public static P1 Supplier_P1(final Supplier s) { + return P.lazy(s::get); + } + + public static F, P1> Supplier_P1() { + return Java8::Supplier_P1; + } + + public static Supplier P1_Supplier(final P1 p) { + return p::_1; + } + + public static F, Supplier> P1_Supplier() { + return Java8::P1_Supplier; + } + + public static F Function_F(final Function f) { + return f::apply; + } + + public static F, F> Function_F() { + return Java8::Function_F; + } + + public static Function F_Function(final F f) { + return f::f; + } + + public static F, Function> F_Function() { + return Java8::F_Function; + } + + public static F2 BiFunction_F2(final BiFunction f) { + return f::apply; + } + + public static F, F2> BiFunction_F2() { + return Java8::BiFunction_F2; + } + + public static BiFunction F2_BiFunction(final F2 f) { + return f::f; + } + + public static F, BiFunction> F2_BiFunction() { + return Java8::F2_BiFunction; + } + + public static Supplier> TryCatch0_Supplier(final Try0 t) { + return () -> Try.f(t)._1(); + } + + public static F, Supplier>> TryCatch0_Supplier() { + return Java8::TryCatch0_Supplier; + } + + public static Function> TryCatch1_Function(final Try1 t) { + return a -> Try.f(t).f(a); + } + + public static F, Function>> TryCatch1_Function() { + return Java8::TryCatch1_Function; + } + + public static BiFunction> TryCatch2_BiFunction(final Try2 t) { + return (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); + } + + public static Option Optional_Option(final Optional o) { + return o.isPresent() ? Option.some(o.get()) : Option.none(); + } + + public static F, Option> Optional_Option() { + return Java8::Optional_Option; + } + + /** + * 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); + } + + public static F, Optional> Option_Optional() { + return Java8::Option_Optional; + } + + public static F, F> Consumer_F() { + return Java8::Consumer_F; + } + + public static F Consumer_F(final Consumer c) { + return a -> { + c.accept(a); + return Unit.unit(); + }; + } + + public static java.util.stream.Stream Stream_JavaStream(final Stream s) { + return Iterable_JavaStream(s); + } + + public static java.util.stream.Stream Iterable_JavaStream(final Iterable it) { + return StreamSupport.stream(it.spliterator(), false); + } + + public static java.util.stream.Stream Iterator_JavaStream(final Iterator it) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false); + } + + public static F, java.util.stream.Stream> Stream_JavaStream() { + return Java8::Stream_JavaStream; + } + + public static Stream JavaStream_Stream(final java.util.stream.Stream s) { + return Stream.iteratorStream(s.iterator()); + } + + public static List JavaStream_List(final java.util.stream.Stream s) { + return s.collect(Collectors.toList()); + } + + 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 ae375828..3e393cf8 100644 --- a/core/src/main/java/fj/data/LazyString.java +++ b/core/src/main/java/fj/data/LazyString.java @@ -1,13 +1,10 @@ package fj.data; -import fj.Equal; -import fj.F; -import fj.F2; +import fj.*; + import static fj.Function.compose; import static fj.Function.curry; import static fj.P.p; -import fj.P1; -import fj.P2; import static fj.data.Option.none; import static fj.data.Option.some; import static fj.data.Stream.join; @@ -35,14 +32,12 @@ private LazyString(final Stream s) { * @return A lazy string with the characters from the given string. */ public static LazyString str(final String s) { - return new LazyString(Stream.unfold(new F, Option>>>() { - public Option>> f(final P2 o) { - final String s = o._1(); + return new LazyString(Stream.unfold(o -> { + final String s2 = o._1(); final int n = o._2(); final Option>> none = none(); - return s.length() <= n ? none : some(p(s.charAt(n), p(s, n + 1))); - } - }, p(s, 0))); + return s2.length() <= n ? none : some(p(s2.charAt(n), p(s2, n + 1))); + }, p(s, 0))); } /** @@ -107,8 +102,23 @@ public CharSequence subSequence(final int start, final int end) { * * @return The String representation of this lazy string. */ + public String toStringEager() { + final StringBuilder builder = new StringBuilder(length() + 16); + s.foreachDoEffect(c -> builder.append(c.charValue())); + return builder.toString(); + } + + public String toStringLazy() { + return s.isEmpty() ? "" : "LazyString(" + Show.charShow.showS(s.head()) + ", ?)"; + } + + @Override public String toString() { - return new StringBuilder(this).toString(); + return toStringLazy(); + } + + public String eval() { + return toStringEager(); } /** @@ -168,11 +178,7 @@ public boolean startsWith(final LazyString cs) { * @return A function that yields true if the first argument is a prefix of the second. */ public static F> startsWith() { - return curry(new F2() { - public Boolean f(final LazyString needle, final LazyString haystack) { - return haystack.startsWith(needle); - } - }); + return curry((needle, haystack) -> haystack.startsWith(needle)); } /** @@ -218,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)); } /** @@ -250,12 +256,16 @@ 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() - : Stream.cons(fromStream(ws._1()), new P1>() { - public Stream _1() { - return fromStream(ws._2()).split(p); - } - }); + return findIt.isEmpty() ? Stream.nil() + : Stream.cons(fromStream(ws._1()), () -> fromStream(ws._2()).split(p)); + } + + public LazyString map(F f) { + return fromStream(s.map(f)); + } + + public LazyString bind(F f) { + return fromStream(s.bind(c -> f.f(c).toStream())); } /** @@ -286,6 +296,10 @@ public Stream lines() { return split('\n'); } + public static F> lines_() { + return LazyString::lines; + } + /** * Joins the given stream of lazy strings into one, separated by newlines. * @@ -296,6 +310,10 @@ public static LazyString unlines(final Stream str) { return fromStream(join(str.intersperse(str("\n")).map(toStream))); } + public static F, LazyString> unlines_() { + return LazyString::unlines; + } + /** * Joins the given stream of lazy strings into one, separated by spaces. * @@ -310,31 +328,19 @@ public static LazyString unwords(final Stream str) { * First-class conversion from lazy strings to streams. */ public static final F> toStream = - new F>() { - public Stream f(final LazyString string) { - return string.toStream(); - } - }; + LazyString::toStream; /** * First-class conversion from lazy strings to String. */ public static final F toString = - new F() { - public String f(final LazyString string) { - return string.toString(); - } - }; + LazyString::toString; /** * First-class conversion from character streams to lazy strings. */ public static final F, LazyString> fromStream = - new F, LazyString>() { - public LazyString f(final Stream s) { - return fromStream(s); - } - }; + LazyString::fromStream; private static final Equal> eqS = streamEqual(charEqual); diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index a957bc5d..721ee9f1 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -1,49 +1,33 @@ package fj.data; +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 java.lang.Class; +import java.util.*; + import static fj.Bottom.error; -import fj.F2Functions; -import fj.Effect; -import fj.Equal; -import fj.F; -import fj.F2; -import fj.F3; -import fj.Function; -import fj.Hash; -import fj.Monoid; -import fj.Ord; -import fj.P; -import fj.P1; -import fj.P2; -import fj.Show; -import fj.Unit; -import static fj.Function.curry; -import static fj.Function.constant; -import static fj.Function.identity; -import static fj.Function.compose; -import static fj.P.p; -import static fj.P.p2; +import static fj.Function.*; +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.Ordering; -import fj.control.Trampoline; -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() { @@ -79,15 +63,7 @@ public final Iterator iterator() { * @return The length of this list. */ public final int length() { - return foldLeft(new F>() { - public F f(final Integer i) { - return new F() { - public Integer f(final A a) { - return i + 1; - } - }; - } - }, 0); + return foldLeft((i, a) -> i + 1, 0); } /** @@ -108,15 +84,8 @@ public final boolean isNotEmpty() { return this instanceof Cons; } - /** - * Performs a reduction on this list using the given arguments. - * - * @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. - */ - public final B list(final B nil, final F, B>> cons) { - return isEmpty() ? nil : cons.f(head()).f(tail()); + public final B uncons(final F2, B> cons, final B nil) { + return isEmpty() ? nil : cons.f(head(), tail()); } /** @@ -125,8 +94,8 @@ public final B list(final B nil, final F, B>> cons) { * @param a The argument to return if this list is empty. * @return The head of this list if there is one or the given argument if this list is empty. */ - public final A orHead(final P1 a) { - return isEmpty() ? a._1() : head(); + public final A orHead(final F0 a) { + return isEmpty() ? a.f() : head(); } /** @@ -135,18 +104,17 @@ public final A orHead(final P1 a) { * @param as The argument to return if this list is empty. * @return The tail of this list if there is one or the given argument if this list is empty. */ - public final List orTail(final P1> as) { - return isEmpty() ? as._1() : tail(); + public final List orTail(final F0> as) { + return isEmpty() ? as.f() : tail(); } /** - * Returns an option projection of this list; None if empty, or the first element in - * Some. + * Returns the head of the list, if any. * - * @return An option projection of this list. + * @return The optional head of the list. */ - public final Option toOption() { - return isEmpty() ? Option.none() : some(head()); + public final Option headOption() { + return isEmpty() ? Option.none() : some(head()); } /** @@ -156,8 +124,8 @@ public final Option toOption() { * @param x The value to return in left if this list is empty. * @return An either projection of this list. */ - public final Either toEither(final P1 x) { - return isEmpty() ? Either.left(x._1()) : Either.right(head()); + public final Either toEither(final F0 x) { + return isEmpty() ? Either.left(x.f()) : Either.right(head()); } /** @@ -166,16 +134,7 @@ public final Either toEither(final P1 x) { * @return A stream projection of this list. */ public final Stream toStream() { - final Stream nil = Stream.nil(); - return foldRight(new F, Stream>>() { - public F, Stream> f(final A a) { - return new F, Stream>() { - public Stream f(final Stream as) { - return as.cons(a); - } - }; - } - }, nil); + return isEmpty() ? Stream.nil() : Stream.cons(head(), () -> tail().toStream()); } /** @@ -183,16 +142,20 @@ public Stream f(final Stream as) { * * @return A array projection of this list. */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public final Array toArray() { - final Object[] a = new Object[length()]; + return mkArray(toArrayObject()); + } + + public final Object[] toArrayObject() { + final int length = length(); + final Object[] a = new Object[length]; List x = this; - for (int i = 0; i < length(); i++) { + for (int i = 0; i < length; i++) { a[i] = x.head(); x = x.tail(); } - - return mkArray(a); + return a; } /** @@ -230,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); } /** @@ -241,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); } /** @@ -382,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()); } /** @@ -396,11 +359,7 @@ public final P2, List> span(final F p) { * the given predicate and the second element is the remainder of the list. */ public final P2, List> breakk(final F p) { - return span(new F() { - public Boolean f(final A a) { - return !p.f(a); - } - }); + return span(a -> !p.f(a)); } /** @@ -467,11 +426,7 @@ public final List bind(final List lb, final F2 f) { * @return The given function, promoted to operate on lists. */ public static F, F, List>> liftM2(final F> f) { - return curry(new F2, List, List>() { - public List f(final List as, final List bs) { - return as.bind(bs, f); - } - }); + return curry((as, bs) -> as.bind(bs, f)); } /** @@ -586,17 +541,353 @@ public final List sequence(final List bs) { } /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * Sequence the given list and collect the output as an IO. + * + * @param list the given list + * @param the type of the IO value + * @return the IO + */ + public static final IO> sequenceIO(final List> list) { + return list.traverseIO(identity()); + } + + /** + * Sequence the given list and collect the output as an list. + * + * @param list the given list + * @param the type of the list value + * @return the list + */ + public static final List> sequenceList(final List> list) { + return list.traverseList(identity()); + } + + /** + * Sequence the given list and collect the output as an list. + * + * @param list the given list + * @param the type of the list value + * @return the list + */ + public static final Option> sequenceOption(final List> list) { + return list.traverseOption(identity()); + } + + /** + * Sequence the given list and collect the output as a P1. + * + * @param list the given list + * @param the type of the P1 value + * @return the P1 + */ + public static final P1> sequenceP1(final List> list) { + return list.traverseP1(identity()); + } + + /** + * Sequence the given list and collect the output as a seq. + * + * @param list the given list + * @param the type of the seq value + * @return the seq + */ + public static final Seq> sequenceSeq(final List> list) { + return list.traverseSeq(identity()); + } + + /** + * Sequence the given list and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param list the given list + * @param the type of the set value + * @return the either + */ + public static final Set> sequenceSet(final Ord ord, final List> list) { + return list.traverseSet(ord, identity()); + } + + /** + * Sequence the given list and collect the output as a stream. + * + * @param list the given list + * @param the type of the stream value + * @return the stream + */ + public static final Stream> sequenceStream(final List> list) { + return list.traverseStream(identity()); + } + + /** + * Sequence the given list and collect the output as a trampoline. + * + * @param list the given trampoline + * @param the type of the stream value + * @return the stream + */ + public static final Trampoline> sequenceTrampoline(final List> list) { + return list.traverseTrampoline(identity()); + } + + /** + * Sequence the given list and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param list the given list + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static final Validation> sequenceValidation(final Semigroup semigroup, final List> list) { + return list.traverseValidation(semigroup, identity()); + } + + /** + * Traverse through the List with given function. + * + * @param f The function that produces Either value. + * @return error in left or f mapped list in right. + */ + public final Either> traverseEither(final F> f) { + return foldRight( + (a, acc) -> f.f(a).right().bind(e -> acc.right().map(es -> es.cons(e))), + Either.right(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either, R> traverseEitherLeft(final F> f) { + return foldRight( + (element, either) -> f.f(element).left().bind(elementInner -> either.left().map(list -> list.cons(elementInner))), + left(nil())); + } + + /** + * Traverse this list with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either> traverseEitherRight(final F> f) { + return foldRight( + (element, either) -> f.f(element).right().bind(elementInner -> either.right().map(list -> list.cons(elementInner))), + right(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public final F> traverseF(F> f) { + return this.foldRight( + (a, acc) -> Function.bind(acc, + (bs) -> Function.compose(bs::cons, f.f(a))), + constant(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public final IO> traverseIO(F> f) { + return this.foldRight( + (a, acc) -> IOFunctions.bind(f.f(a), b -> IOFunctions.map(acc, bs -> bs.cons(b))), + IOFunctions.unit(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public final List> traverseList(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), + single(List.nil())); + } + + /** + * Traverses through the List with the given function + * + * @param f The function that produces Option value + * @return none if applying f returns none to any element of the list or f mapped list in some . + */ + public final Option> traverseOption(final F> f) { + return foldRight( + (a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))), + some(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public final P1> traverseP1(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), + p(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public final Seq> traverseSeq(final F> f) { + return foldRight( + (element, seq) -> f.f(element).bind(elementInner -> seq.map(list -> list.cons(elementInner))), + Seq.single(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public final Set> traverseSet(final Ord ord, final F> f) { + final Ord> listOption = Ord.listOrd(ord); + return foldRight( + (element, set) -> f.f(element).bind(listOption, elementInner -> set.map(listOption, list -> list.cons(elementInner))), + Set.single(listOption, nil())); + } + + /** + * Traverse this list with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public final Stream> traverseStream(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(s -> acc.map(ss -> ss.cons(s))), + Stream.single(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public final Trampoline> traverseTrampoline(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), + 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 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())); + } + + /** * Performs function application within a list (applicative functor pattern). * * @param lf The list of functions to apply. * @return A new list after applying the given list of functions through this list. */ public final List apply(final List> lf) { - return lf.bind(new F, List>() { - public List f(final F f) { - return map(f); - } - }); + return lf.bind(this::map); } /** @@ -606,18 +897,18 @@ public List f(final F f) { * @return A new list that has appended the given list. */ public final List append(final List as) { - return fromList(this).append(as).toList(); + return fromList(this).prependToList(as); } /** - * Performs a right-fold reduction across this list. This function uses O(length) stack space. + * Performs a right-fold reduction across this list. * * @param f The function to apply on each element of the list. * @param b The beginning value to start the application from. * @return The final result after the right-fold reduction. */ public final B foldRight(final F> f, final B b) { - return isEmpty() ? b : f.f(head()).f(tail().foldRight(f, b)); + return foldRight(uncurryF2(f), b); } /** @@ -628,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); } /** @@ -638,11 +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(new P1>() { - public Trampoline _1() { - return 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()))); } /** @@ -653,13 +940,7 @@ public Trampoline _1() { * @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); } /** @@ -670,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; } /** @@ -681,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()); } /** @@ -692,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)); } /** @@ -703,15 +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(new F, F>>() { - public F> f(final List as) { - return new F>() { - public List f(final A a) { - return cons(a, as); - } - }; - } - }, List.nil()); + return foldLeft((as, a) -> cons(a, as), nil()); } /** @@ -741,7 +1020,15 @@ 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) { - return i <= 0 || isEmpty() ? List.nil() : cons(head(), tail().take(i - 1)); + Buffer result = empty(); + List list = this; + int index = i; + while (index > 0 && list.isNotEmpty()) { + result.snoc(list.head()); + list = list.tail(); + index--; + } + return result.toList(); } /** @@ -751,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; @@ -769,24 +1054,19 @@ public final List drop(final int i) { * @return A pair of lists split at the given index of this list. */ public final P2, List> splitAt(final int i) { - P2, List> s = p(List.nil(), List.nil()); - int c = 0; + List first = List.nil(); + List second = nil(); for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) { final A h = xs.head(); - s = c < i ? s.map1(new F, List>() { - public List f(final List as) { - return as.snoc(h); - } - }) : s.map2(new F, List>() { - public List f(final List as) { - return as.snoc(h); - } - }); + if (c < i) { + first = first.cons(h); + } else { + second = second.cons(h); + } c++; } - - return s; + return p(first.reverse(), second.reverse()); } /** @@ -801,11 +1081,23 @@ 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(new F, Option, List>>>() { - public Option, List>> f(final List as) { - return as.isEmpty() ? Option., List>>none() : some(as.splitAt(n)); - } - }, this); + return unfold(as -> as.isEmpty() ? Option.none() : some(as.splitAt(n)), this); + } + + /** + * Partitions the list into a tuple where the first element contains the + * items that satisfy the the predicate f and the second element contains the + * items that does not. The relative order of the elements in the returned tuple + * is the same as the original list. + * + * @param f Predicate function. + */ + 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(p2._1().reverse(), p2._2().reverse()); } /** @@ -814,7 +1106,7 @@ public Option, List>> f(final List as) { * @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; @@ -826,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()); } /** @@ -841,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(); @@ -859,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 { @@ -873,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); } } @@ -918,11 +1210,7 @@ public final List zipWith(final List bs, final F2 f) { * @return The first-class version of zipWith */ public static F, F, F>, List>>> zipWith() { - return curry(new F3, List, F>, List>() { - public List f(final List as, final List bs, final F> f) { - return as.zipWith(bs, f); - } - }); + return curry((as, bs, f) -> as.zipWith(bs, f)); } /** @@ -943,11 +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(new F2, List, List>>() { - public List> f(final List as, final List bs) { - return as.zip(bs); - } - }); + return curry(List::zip); } /** @@ -956,15 +1240,7 @@ public List> f(final List as, final List bs) { * @return A new list with the same length as this list. */ public final List> zipIndex() { - return zipWith(range(0, length()), new F>>() { - public F> f(final A a) { - return new F>() { - public P2 f(final Integer i) { - return p(a, i); - } - }; - } - }); + return zipWith(range(0, length()), a -> i -> p(a, i)); } /** @@ -1027,7 +1303,7 @@ public final Option find(final F f) { public final List intersperse(final A a) { return isEmpty() || tail().isEmpty() ? this : - cons(head(), cons(a, tail().intersperse(a))); + cons(head(), tail().bind(a2 -> list(a, a2))); } /** @@ -1036,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)); } @@ -1047,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()); } /** @@ -1057,11 +1333,7 @@ public final List nub() { * @return A list without duplicates. */ public final List nub(final Equal eq) { - return isEmpty() ? this : cons(head(), tail().filter(new F() { - public Boolean f(final A a) { - return !eq.eq(a, head()); - } - }).nub(eq)); + return isEmpty() ? this : cons(head(), tail().filter(a -> !eq.eq(a, head())).nub(eq)); } /** @@ -1070,35 +1342,37 @@ public Boolean f(final A a) { * @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_()); } + /** * First-class head function. * * @return A function that gets the head of a given list. */ public static F, A> head_() { - return new F, A>() { - public A f(final List list) { - return list.head(); - } - }; + return List::head; } + + /** + * Reutrns the tail of the list, if any. + * @return The optional tail of the list. + */ + public final Option> tailOption() { + return isEmpty() ? none() : some(tail()); + } + /** * First-class tail function. * * @return A function that gets the tail of a given list. */ public static F, List> tail_() { - return new F, List>() { - public List f(final List list) { - return list.tail(); - } - }; + return List::tail; } /** @@ -1112,7 +1386,9 @@ public final List minus(final Equal eq, final List xs) { return removeAll(compose(Monoid.disjunctionMonoid.sumLeft(), xs.mapM(curry(eq.eq())))); } - /** + + + /** * Maps the given function of arity-2 across this list and returns a function that applies all the resulting * functions to a given argument. * @@ -1131,19 +1407,7 @@ public final F> mapM(final F> f) { * @return A possible list of values after binding through the Option monad. */ public final Option> mapMOption(final F> f) { - return foldRight(new F2>, Option>>() { - public Option> f(final A a, final Option> bs) { - return f.f(a).bind(new F>>() { - public Option> f(final B b) { - return bs.map(new F, List>() { - public List f(final List bbs) { - return bbs.cons(b); - } - }); - } - }); - } - }, Option.>some(List.nil())); + return traverseOption(f); } /** @@ -1153,19 +1417,7 @@ public List f(final List bbs) { * @return A list of values in the Trampoline monad. */ public final Trampoline> mapMTrampoline(final F> f) { - return foldRight(new F2>, Trampoline>>() { - public Trampoline> f(final A a, final Trampoline> bs) { - return f.f(a).bind(new F>>() { - public Trampoline> f(final B b) { - return bs.map(new F, List>() { - public List f(final List bbs) { - return 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())); } /** @@ -1202,7 +1454,7 @@ public final List init() { List ys = this; final Buffer a = empty(); while(ys.isNotEmpty() && ys.tail().isNotEmpty()) { - a.snoc(head()); + a.snoc(ys.head()); ys = ys.tail(); } return a.toList(); @@ -1234,30 +1486,132 @@ 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.comap(List.length_())).head(); + 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}. + * + * @param keyFunction The function to select the keys for the map. + * @param keyOrd An order for the keys of the tree map. + * @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, 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 + * the keyOrd parameter. + * + * @param keyFunction The function to select the keys for the map. + * @param valueFunction The function to apply on each matching value. + * @param keyOrd An order for the keys of the tree map. + * @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, + final Ord keyOrd) { + return this.groupBy(keyFunction, valueFunction, List.nil(), List::cons, 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 + * the keyOrd parameter. + * + * @param keyFunction The function to select the keys for the map. + * @param valueFunction The function to apply on each matching value. + * @param monoid A monoid, which defines the accumulator for the values and the zero value. + * @param keyOrd An order for the keys of the tree map. + * @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, + final Monoid monoid, + final Ord keyOrd) { + return groupBy(keyFunction, valueFunction, monoid.zero(), + uncurryF2(monoid.sum()), keyOrd); + } + + /** + * Groups the elements of this list by a given keyFunction, applies the valueFunction and + * accumulates the mapped values with the given grouping accumulator function on the grouping + * identity. + * + * @param keyFunction The function to select the keys. + * @param valueFunction The function to apply on each element. + * @param groupingIdentity The identity, or start value, for the grouping. + * @param groupingAcc The accumulator to apply on each matching value. + * @param keyOrd An order for the keys of the tree map. + * @return A TreeMap containing the keys with the accumulated result of matched and mapped + * elements. + */ + public final TreeMap groupBy( + final F keyFunction, + final F valueFunction, + final D groupingIdentity, + final F2 groupingAcc, + final Ord 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); + } + + + /** * Returns whether or not all elements in the list are equal according to the given equality test. * * @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); } + public final boolean isPrefixOf(final Equal eq, final List xs) { + final Iterator i = iterator(); + final Iterator j = xs.iterator(); + + while (i.hasNext() && j.hasNext()) { + if (!eq.eq(i.next(), j.next())) { + return false; + } + } + + return !i.hasNext(); + } + + public final boolean isSuffixOf(final Equal eq, final List xs) { + final Iterator i = iterator(); + final Iterator j = xs.drop(xs.length() - length()).iterator(); + + while (i.hasNext() && j.hasNext()) { + if (!eq.eq(i.next(), j.next())) { + return false; + } + } + + return !i.hasNext(); + } + /** * First-class length. * * @return A function that gets the length of a given list. */ public static F, Integer> length_() { - return new F, Integer>() { - public Integer f(final List a) { - return a.length(); - } - }; + return List::length; } /** @@ -1267,7 +1621,17 @@ public Integer f(final List a) { * @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)); } /** @@ -1277,7 +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()); } /** @@ -1318,6 +1696,8 @@ public int size() { } private static final class Nil extends List { + public static final Nil INSTANCE = new Nil<>(); + public A head() { throw error("head on empty list"); } @@ -1355,17 +1735,33 @@ private void tail(final List tail) { * @param as The elements to construct a list with. * @return A list with the given elements. */ - public static List list(final A... as) { + @SafeVarargs public static List list(final A... as) { + return arrayList(as); + } + + /** + * Constructs a list from the given elements. + */ + @SafeVarargs + public static List arrayList(final A... as) { return Array.array(as).toList(); } + /** + * Constructs a list from the given Iterator. + */ + public static List fromIterator(final Iterator it) { + return iterableList(() -> it); + } + /** * Returns an empty list. * * @return An empty list. */ + @SuppressWarnings("unchecked") public static List nil() { - return new Nil(); + return (Nil) Nil.INSTANCE; } /** @@ -1374,19 +1770,11 @@ public static List nil() { * @return A function that prepends (cons) an element to a list to produce a new list. */ public static F, List>> cons() { - return new F, List>>() { - public F, List> f(final A a) { - return new F, List>() { - public List f(final List tail) { - return cons(a, tail); - } - }; - } - }; + return a -> tail -> cons(a, tail); } public static F2, List> cons_() { - return (a, listA) -> cons(a, listA); + return List::cons; } /** @@ -1396,11 +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 new F>() { - public List f(final A a) { - return tail.cons(a); - } - }; + return tail::cons; } /** @@ -1410,11 +1794,7 @@ public List f(final A a) { * @return A function that prepends the given value to a list. */ public static F, List> cons_(final A a) { - return new F, List>() { - public List f(final List as) { - return as.cons(a); - } - }; + return as -> as.cons(a); } /** @@ -1425,7 +1805,7 @@ public List f(final List as) { * @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); } /** @@ -1434,11 +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 new F, Boolean>() { - public Boolean f(final List as) { - return as.isEmpty(); - } - }; + return List::isEmpty; } /** @@ -1447,11 +1823,7 @@ public Boolean f(final List as) { * @return A function that determines whether a given list is not empty. */ public static F, Boolean> isNotEmpty_() { - return new F, Boolean>() { - public Boolean f(final List as) { - return as.isNotEmpty(); - } - }; + return List::isNotEmpty; } /** @@ -1471,11 +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 new F>, List>() { - public List f(final List> as) { - return join(as); - } - }; + return List::join; } /** @@ -1506,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()); } /** @@ -1517,7 +1885,9 @@ 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) { - return n <= 0 ? List.nil() : replicate(n - 1, a).cons(a); + List list = nil(); + for (int i = 0; i < n; i++) { list = list.cons(a); } + return list; } /** @@ -1530,7 +1900,11 @@ public static List replicate(final int n, final A a) { * to value (exclusive). */ public static List range(final int from, final int to) { - return from >= to ? List.nil() : cons(from, range(from + 1, to)); + final Buffer buf = empty(); + for (int i = from; i < to; i++) { + buf.snoc(i); + } + return buf.toList(); } /** @@ -1555,11 +1929,7 @@ public static List fromString(final String s) { * @return A first-class fromString. */ public static F> fromString() { - return new F>() { - public List f(final String s) { - return fromString(s); - } - }; + return List::fromString; } /** @@ -1572,11 +1942,9 @@ public List f(final String s) { public static String asString(final List cs) { final StringBuilder sb = new StringBuilder(); - cs.foreach(new F() { - public Unit f(final Character c) { - sb.append(c); - return unit(); - } + cs.foreach(c -> { + sb.append(c); + return unit(); }); return sb.toString(); } @@ -1587,11 +1955,7 @@ public Unit f(final Character c) { * @return A first-class asString. */ public static F, String> asString() { - return new F, String>() { - public String f(final List cs) { - return asString(cs); - } - }; + return List::asString; } /** @@ -1601,7 +1965,7 @@ public String f(final List cs) { * @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()); } /** @@ -1616,16 +1980,8 @@ public static List single(final A a) { */ public static List iterateWhile(final F f, final F p, final A a) { return unfold( - new F>>() { - public Option> f(final A o) { - return Option.iif(new F, Boolean>() { - public Boolean f(final P2 p2) { - return p.f(o); - } - }, P.p(o, f.f(o))); - } - } - , a); + o -> Option.iif(p2 -> p.f(o), p(o, f.f(o))) + , a); } /** @@ -1637,11 +1993,7 @@ public Boolean f(final P2 p2) { * @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(new F, Boolean>() { - public Boolean f(final P2 p) { - return e.eq(p._1(), a); - } - }).map(P2.__2()); + return x.find(p -> e.eq(p._1(), a)).map(P2.__2()); } /** @@ -1651,11 +2003,7 @@ public Boolean f(final P2 p) { * @return A partially applied version of {@link #lookup(Equal , List, Object)}. */ public static F2>, A, Option> lookup(final Equal e) { - return new F2>, A, Option>() { - public Option f(final List> x, final A a) { - return lookup(e, x, a); - } - }; + return (x, a) -> lookup(e, x, a); } /** @@ -1664,11 +2012,7 @@ public Option f(final List> x, final A a) { * @return The bind function for lists. */ public static F>, F, List>> bind_() { - return curry(new F2>, List, List>() { - public List f(final F> f, final List as) { - return as.bind(f); - } - }); + return curry((f, as) -> as.bind(f)); } /** @@ -1677,11 +2021,7 @@ public List f(final F> f, final List as) { * @return The map function for lists. */ public static F, F, List>> map_() { - return curry(new F2, List, List>() { - public List f(final F f, final List as) { - return as.map(f); - } - }); + return curry((f, as) -> as.map(f)); } /** @@ -1692,8 +2032,8 @@ public List f(final F f, final List as) { * 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())); } /** @@ -1702,11 +2042,7 @@ public static F> sequence_(final List> fs) { * @return The left fold function for lists. */ public static F>, F, B>>> foldLeft() { - return curry(new F3>, B, List, B>() { - public B f(final F> f, final B b, final List as) { - return as.foldLeft(f, b); - } - }); + return curry((f, b, as) -> as.foldLeft(f, b)); } /** @@ -1715,11 +2051,7 @@ public B f(final F> f, final B b, final List as) { * @return First-class version of take. */ public static F, List>> take() { - return curry(new F2, List>() { - public List f(final Integer n, final List as) { - return as.take(n); - } - }); + return curry((n, as) -> as.take(n)); } /** @@ -1730,13 +2062,18 @@ public List f(final Integer n, final List as) { */ public static List iterableList(final Iterable i) { final Buffer bs = empty(); - - for (final A a : i) + for (final A a : i) { bs.snoc(a); - + } return bs.toList(); } + /** + * Constructs a list from the given Iterator. + */ + public static List iteratorList(final Iterator it) { + return iterableList(() -> it); + } /** * A mutable, singly linked list. This structure should be used very sparingly, in favour @@ -1760,13 +2097,13 @@ public Iterator iterator() { * Appends (snoc) the given element to this buffer to produce a new buffer. * * @param a The element to append to this buffer. - * @return A new buffer with the given element appended. + * @return This buffer. */ 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; @@ -1779,10 +2116,10 @@ public Buffer snoc(final A a) { } /** - * Appends the given buffer to this buffer. + * Appends the given list to this buffer. * - * @param as The buffer to append to this one. - * @return A new buffer that has appended the given buffer. + * @param as The list to append to this buffer. + * @return This buffer. */ public Buffer append(final List as) { for (List xs = as; xs.isNotEmpty(); xs = xs.tail()) @@ -1791,6 +2128,28 @@ public Buffer append(final List as) { return this; } + /** + * Prepends the elements of this buffer to the given list. + * + * @param as the list to which elements are prepended. + */ + public List prependToList(final List as) { + if (isEmpty()) { + return as; + } else { + if (exported) + copy(); + + tail.tail(as); + return toList(); + } + } + + /** + * Returns true if this buffer is empty, false otherwise. + */ + public boolean isEmpty() { return start.isEmpty(); } + /** * Returns an immutable list projection of this buffer. Modifications to the underlying buffer * will not be reflected in returned lists. @@ -1817,7 +2176,7 @@ public Collection toCollection() { * @return An empty buffer. */ public static Buffer empty() { - return new Buffer(); + return new Buffer<>(); } /** @@ -1827,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()); @@ -1850,11 +2209,12 @@ public static Buffer iterableBuffer(final Iterable i) { return b; } - @SuppressWarnings({"ObjectEquality"}) + @SuppressWarnings("ObjectEquality") private void copy() { List s = start; final Cons t = tail; start = nil(); + tail = null; exported = false; while (s != t) { snoc(s.head()); @@ -1873,14 +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 */ - //Suppress the warning for cast to List because the type is checked in the previous line. - @SuppressWarnings({ "unchecked" }) - @Override public boolean equals( final Object obj ) { - if ( obj == null || !( obj instanceof List ) ) { return false; } - - //Casting to List here does not cause a runtime exception even if the type arguments don't match. - //The cast is done to avoid the compiler warning "raw use of parameterized class 'List'" - return Equal.listEqual( Equal.anyEqual() ).eq( this, (List) obj ); + @Override public final boolean equals(final Object obj) { + return Equal.equals0(List.class, this, obj, () -> Equal.listEqual(Equal.anyEqual())); } /** @@ -1889,8 +2243,9 @@ private void copy() { * * @return the hash code for this list. */ - @Override public int hashCode() { - return Hash.listHash( Hash.anyHash() ).hash( this ); + @Override + public final int hashCode() { + return Hash.listHash(Hash.anyHash()).hash(this); } /** @@ -1899,11 +2254,134 @@ private void copy() { * * @return a String representation of the list */ - @Override public String toString() { - return Show.listShow( Show.anyShow() ).show( this ).foldLeft( new F2() { - @Override public String f( final String s, final Character c ) { - return s + c; - } - }, "" ); + @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 final boolean isSingle() { + return isNotEmpty() && tail().isEmpty(); + } + + /** + * Optic factory methods for a List + */ + public static final class Optic { + + private Optic() { + throw new UnsupportedOperationException(); + } + + /** + * Polymorphic traversal + */ + public static PTraversal, List, A, B> pTraversal() { + return new PTraversal, List, A, B>() { + + @Override + public F, F>> modifyFunctionF(F> f) { + return l -> l.traverseF(f); + } + + @Override + public F, Either>> modifyEitherF(F> f) { + return l -> l.traverseEither(f); + } + + @Override + public F, IO>> modifyIOF(F> f) { + return l -> l.traverseIO(f); + } + + @Override + public F, Trampoline>> modifyTrampolineF(F> f) { + return l -> l.traverseTrampoline(f); + } + + @Override + public F, Promise>> modifyPromiseF(F> f) { + return l -> l.traversePromise(f); + } + + @Override + public F, List>> modifyListF(F> f) { + return l -> l.traverseList(f); + } + + @Override + public F, Option>> modifyOptionF(F> f) { + return l -> l.traverseOption(f); + } + + @Override + public F, Stream>> modifyStreamF(F> f) { + return l -> l.traverseStream(f); + } + + @Override + public F, P1>> modifyP1F(F> f) { + return l -> l.traverseP1(f); + } + + @Override + public F, Validation>> modifyValidationF(Semigroup s, F> f) { + return l -> l.traverseValidation(s, f); + } + + @Override + public F, V2>> modifyV2F(F> f) { + return l -> l.traverseV2(f); + } + + @Override + public F, M> foldMap(Monoid monoid, F f) { + return l -> monoid.sumLeft(l.map(f)); + } + }; } + + /** + * Monomorphic traversal + */ + public static Traversal, A> traversal() { + return new Traversal<>(pTraversal()); + } + + /** + * Optional targeted on Cons head. + */ + public static Optional, A> head() { + 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.uncons((__, tail) -> some(tail), none()), + tail -> l -> l.uncons((h, __) -> List.cons(h, tail), l)); + } + + /** + * Nil prism + */ + public static Prism, Unit> nil() { + return prism((List l) -> l.isEmpty() ? some(unit()) : none(), constant(List.nil())); + } + + /** + * Cons prism + */ + public static Prism, P2>> cons() { + return prism(l -> l.>>> uncons((h, tail) -> some(p(h, tail)), none()), c -> List.cons(c._1(), c._2())); + } + + } + + public static final class Unsafe { + + } + } diff --git a/core/src/main/java/fj/data/Natural.java b/core/src/main/java/fj/data/Natural.java index 98dddb25..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,11 +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 = - new F>() { - public Option f(final BigInteger i) { - return natural(i); - } - }; + Natural::natural; /** * Returns the natural number equal to the given long @@ -81,11 +82,7 @@ public Natural succ() { * @return A function that returns the successor of a given natural number. */ public static F succ_() { - return new F() { - public Natural f(final Natural natural) { - return natural.succ(); - } - }; + return Natural::succ; } /** @@ -103,11 +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 new F>() { - public Option f(final Natural natural) { - return natural.pred(); - } - }; + return Natural::pred; } /** @@ -123,11 +116,7 @@ public Natural add(final Natural n) { /** * A function that adds two natural numbers. */ - public static final F> add = curry(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return n1.add(n2); - } - }); + public static final F> add = curry(Natural::add); /** @@ -137,18 +126,14 @@ public Natural f(final Natural n1, final Natural n2) { * @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)); } /** * A function that subtracts its first argument from its second. */ public static final F>> subtract = - curry(new F2>() { - public Option f(final Natural o, final Natural o1) { - return o1.subtract(o); - } - }); + curry((o, o1) -> o1.subtract(o)); /** * Multiply a natural number by another. @@ -163,22 +148,14 @@ public Natural multiply(final Natural n) { /** * A function that multiplies a natural number by another. */ - public static final F> multiply = curry(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return n1.multiply(n2); - } - }); + public static final F> multiply = curry(Natural::multiply); /** * A function that divides its second argument by its first. */ public static final F> divide = - curry(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return n2.divide(n1); - } - }); + curry((n1, n2) -> n2.divide(n1)); /** * Divide a natural number by another. @@ -205,11 +182,7 @@ public Natural mod(final Natural n) { * A function that yields the remainder of division of its second argument by its first. */ public static final F> mod = - curry(new F2() { - public Natural f(final Natural n1, final Natural n2) { - return n2.mod(n1); - } - }); + curry((n1, n2) -> n2.mod(n1)); /** * Divide a natural number by another yielding both the quotient and the remainder. @@ -226,11 +199,7 @@ public V2 divmod(final Natural n) { * A function that divides its second argument by its first, yielding both the quotient and the remainder. */ public static final F>> divmod = - curry(new F2>() { - public V2 f(final Natural n1, final Natural n2) { - return n2.divmod(n1); - } - }); + curry((n1, n2) -> n2.divmod(n1)); /** @@ -281,11 +250,7 @@ public int intValue() { /** * A function that returns the BigInteger value of a given Natural. */ - public static final F bigIntegerValue = new F() { - public BigInteger f(final Natural n) { - return n.bigIntegerValue(); - } - }; + public static final F bigIntegerValue = Natural::bigIntegerValue; /** * Sums a stream of natural numbers. @@ -326,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 1c14c535..96bf444a 100644 --- a/core/src/main/java/fj/data/NonEmptyList.java +++ b/core/src/main/java/fj/data/NonEmptyList.java @@ -1,19 +1,19 @@ package fj.data; -import fj.F; -import fj.F1Functions; +import fj.*; import fj.function.Effect1; -import static fj.data.Option.some; -import static fj.data.Option.somes; - 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 { /** @@ -26,17 +26,19 @@ public Iterator iterator() { return toCollection().iterator(); } + private final A head; + + private final List tail; + /** * The first element of this linked list. */ - @SuppressWarnings({"PublicField", "ClassEscapesDefinedScope"}) - public final A head; + public A head() { return head; } /** * This list without the first element. */ - @SuppressWarnings({"PublicField"}) - public final List tail; + public List tail() { return tail; } private NonEmptyList(final A head, final List tail) { this.head = head; @@ -53,6 +55,33 @@ public NonEmptyList cons(final A a) { return nel(a, tail.cons(head)); } + /** + * Appends (snoc) the given element to this non empty list to produce a new non empty list. O(n). + * + * @param a The element to append to this non empty list. + * @return A new non empty list with the given element appended. + */ + public NonEmptyList snoc(final A a) { + return nel(head, tail.snoc(a)); + } + + /** + * The length of this list. + * + * @return The length of this list. + */ + 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. * @@ -60,7 +89,7 @@ public NonEmptyList cons(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); @@ -68,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. * @@ -85,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()); @@ -106,13 +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(new F, Option>>() { - public Option> f(final List list) { - return fromList(list); - } - }, Conversions.Stream_List())).toList())).some(); + .map(f).toList())).some(); } /** @@ -122,11 +180,7 @@ public Option> f(final List list) { * @return A NonEmptyList of the tails of this list. */ public NonEmptyList> tails() { - return fromList(somes(toList().tails().map(new F, Option>>() { - public Option> f(final List list) { - return fromList(list); - } - }))).some(); + return fromList(somes(toList().tails().map(NonEmptyList::fromList))).some(); } /** @@ -139,6 +193,122 @@ public NonEmptyList mapTails(final F, B> f) { return tails().map(f); } + /** + * Intersperses the given argument between each element of this non empty list. + * + * @param a The separator to intersperse in this non empty list. + * @return A non empty list with the given separator interspersed. + */ + public NonEmptyList intersperse(final A a) { + final List list = toList().intersperse(a); + return nel(list.head(), list.tail()); + } + + /** + * Reverse this non empty list in constant stack space. + * + * @return A new non empty list with the elements in reverse order. + */ + public NonEmptyList reverse() { + final List list = toList().reverse(); + return nel(list.head(), list.tail()); + } + + /** + * Sorts this non empty list using the given order over elements using a merge sort algorithm. + * + * @param o The order over the elements of this non empty list. + * @return A sorted non empty list according to the given order. + */ + public NonEmptyList sort(final Ord o) { + final List list = toList().sort(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. + * + * @param bs The non empty list to zip this non empty list with. + * @return A new non empty list with a length the same as the shortest of this list and the given list. + */ + public NonEmptyList> zip(final NonEmptyList bs) { + final List> list = toList().zip(bs.toList()); + return nel(list.head(), list.tail()); + } + + /** + * Zips this non empty list with the index of its element as a pair. + * + * @return A new non empty list with the same length as this list. + */ + public NonEmptyList> zipIndex() { + final List> list = toList().zipIndex(); + return nel(list.head(), list.tail()); + } + + /** + * Zips this non empty list with the given non empty list using the given function to produce a new list. If this list + * and the given list have different lengths, then the longer list is normalised so this function + * never fails. + * + * @param bs The non empty list to zip this non empty list with. + * @param f The function to zip this non empty list and the given non empty list with. + * @return A new non empty list with a length the same as the shortest of this list and the given list. + */ + public NonEmptyList zipWith(final List bs, final F> f) { + final List list = toList().zipWith(bs, f); + return nel(list.head(), list.tail()); + } + + /** + * Zips this non empty list with the given non empty list using the given function to produce a new list. If this list + * and the given list have different lengths, then the longer list is normalised so this function + * never fails. + * + * @param bs The non empty list to zip this non empty list with. + * @param f The function to zip this non empty list and the given non empty list with. + * @return A new non empty list with a length the same as the shortest of this list and the given list. + */ + public NonEmptyList zipWith(final List bs, final F2 f) { + final List list = toList().zipWith(bs, f); + return nel(list.head(), list.tail()); + } + + /** + * Transforms a non empty list of pairs into a non empty list of first components and + * a non empty list of second components. + * + * @param xs The non empty list of pairs to transform. + * @return A non empty list of first components and a non empty list of second components. + */ + public static P2, NonEmptyList> unzip(final NonEmptyList> xs) { + final P2, List> p = List.unzip(xs.toList()); + return P.p(nel(p._1().head(), p._1().tail()), nel(p._2().head(), p._2().tail())); + } + /** * Returns a List projection of this list. * @@ -163,11 +333,7 @@ public Collection toCollection() { * @return A function that takes a non-empty list to a list. */ public static F, List> toList_() { - return new F, List>() { - public List f(final NonEmptyList as) { - return as.toList(); - } - }; + return NonEmptyList::toList; } /** @@ -178,17 +344,18 @@ public List f(final NonEmptyList as) { * @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); } /** - * Return a non-empty list with the given value. + * Constructs a non empty list from the given elements. * - * @param head The value in the non-empty list. - * @return A non-empty list with the given value. + * @param head The first in the non-empty list. + * @param tail The elements to construct a list's tail with. + * @return A non-empty list with the given elements. */ - public static NonEmptyList nel(final A head) { - return nel(head, List.nil()); + @SafeVarargs public static NonEmptyList nel(final A head, final A... tail) { + return nel(head, List.list(tail)); } /** @@ -197,11 +364,7 @@ public static NonEmptyList nel(final A head) { * @return A function that puts an element into a non-empty list. */ public static F> nel() { - return new F>() { - public NonEmptyList f(final A a) { - return nel(a); - } - }; + return a -> nel(a); } /** @@ -212,7 +375,32 @@ public NonEmptyList f(final A a) { */ public static Option> fromList(final List as) { return as.isEmpty() ? - Option.>none() : + Option.none() : some(nel(as.head(), as.tail())); } + + /** + * Concatenate (join) a non empty list of non empty lists. + * + * @param o The non empty list of non empty lists to join. + * @return A new non empty list that is the concatenation of the given lists. + */ + public static NonEmptyList join(final NonEmptyList> o) { return o.bind(identity()); } + + /** + * Perform an equality test on this list which delegates to the .equals() method of the member instances. + * This is implemented with Equal.nonEmptyListEqual using the anyEqual rule. + * + * @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(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual())); + } + + @Override public int hashCode() { + return Hash.nonEmptyListHash(Hash.anyHash()).hash(this); + } + + @Override public String toString() { return Show.nonEmptyListShow(Show.anyShow()).showS(this); } } diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index 4fdbddaa..9831c48c 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -1,54 +1,37 @@ package fj.data; -import static fj.Bottom.error; - -import fj.Effect; -import fj.F; -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 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 static fj.Show.anyShow; - -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() { } - public String toString() { - final Show s = anyShow(); - return optionShow(s).showS(this); + @Override + public final String toString() { + return optionShow(Show.anyShow()).showS(this); } /** @@ -91,11 +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 new F, Boolean>() { - public Boolean f(final Option a) { - return a.isSome(); - } - }; + return Option::isSome; } /** @@ -104,11 +83,7 @@ public Boolean f(final Option a) { * @return A function that returns false if a given optional value has a value, otherwise true. */ public static F, Boolean> isNone_() { - return new F, Boolean>() { - public Boolean f(final Option a) { - return a.isNone(); - } - }; + return Option::isNone; } /** @@ -129,8 +104,8 @@ public final B option(final B b, final F f) { * @param f The function to apply to the value of this optional value. * @return A reduction on this optional value. */ - public final B option(final P1 b, final F f) { - return isSome() ? f.f(some()) : b._1(); + public final B option(final F0 b, final F f) { + return isSome() ? f.f(some()) : b.f(); } /** @@ -148,8 +123,8 @@ public final int length() { * @param a The argument to return if this optiona value has no value. * @return The value of this optional value or the given argument. */ - public final A orSome(final P1 a) { - return isSome() ? some() : a._1(); + public final A orSome(final F0 a) { + return isSome() ? some() : a.f(); } /** @@ -168,11 +143,11 @@ public final A orSome(final A a) { * @param message The message to fail with if this optional value has no value. * @return The value of this optional value if there there is one. */ - public final A valueE(final P1 message) { + public final A valueE(final F0 message) { if(isSome()) return some(); else - throw error(message._1()); + throw error(message.f()); } /** @@ -195,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(); } /** @@ -204,11 +179,7 @@ public final Option map(final F f) { * @return A function that maps a given function across a given optional value. */ public static F, F, Option>> map() { - return curry(new F2, Option, Option>() { - public Option f(final F abf, final Option option) { - return option.map(abf); - } - }); + return curry((abf, option) -> option.map(abf)); } /** @@ -226,7 +197,7 @@ public final Unit foreach(final F f) { * * @param f The side-effect to perform for the given element. */ - public final void foreach(final Effect1 f) { + public final void foreachDoEffect(final Effect1 f) { if (isSome()) f.f(some()); } @@ -239,7 +210,7 @@ public final void foreach(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(); } /** @@ -249,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(); } /** @@ -374,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()); } /** @@ -419,6 +390,333 @@ public final Option sequence(final Option o) { return bind(c); } + /** + * Sequence the given option and collect the output on the right side of an either. + * + * @param option the given option + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEither(final Option> option) { + return option.traverseEitherRight(identity()); + } + + /** + * Sequence the given option and collect the output on the left side of an either. + * + * @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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * 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()); + } + + /** + * Traverse this option with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either> traverseEither(final F> f) { + return traverseEitherRight(f); + } + + /** + * Traverse this option with the given function and collect the output on the left side of an either. + * + * @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)); + } + + /** + * 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; + } + /** * Performs function application within an optional value (applicative functor pattern). * @@ -427,15 +725,7 @@ public final Option sequence(final Option o) { * optional value. */ public final Option apply(final Option> of) { - return of.bind(new F, Option>() { - public Option f(final F f) { - return map(new F() { - public B f(final A a) { - return f.f(a); - } - }); - } - }); + return of.bind(f -> map(f)); } /** @@ -444,8 +734,8 @@ public B f(final A a) { * @param o The optional value to return if this optional value has no value. * @return This optional value if there is one, otherwise, returns the argument optional value. */ - public final Option orElse(final P1> o) { - return isSome() ? this : o._1(); + public final Option orElse(final F0> o) { + return isSome() ? this : o.f(); } /** @@ -465,8 +755,8 @@ public final Option orElse(final Option o) { * @param x The value to return in left if this optional value has no value. * @return An either projection of this optional value. */ - public final Either toEither(final P1 x) { - return isSome() ? Either.right(some()) : Either.left(x._1()); + public final Either toEither(final F0 x) { + return isSome() ? Either.right(some()) : Either.left(x.f()); } /** @@ -477,7 +767,7 @@ public final Either toEither(final P1 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) { @@ -491,11 +781,7 @@ public final Validation toValidation(final X x) { * return in left. */ public static F, F>> toEither() { - return curry(new F2, X, Either>() { - public Either f(final Option a, final X x) { - return a.toEither(x); - } - }); + return curry(Option::toEither); } /** @@ -504,7 +790,7 @@ public Either f(final Option a, final X x) { * @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(); } /** @@ -513,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(); } /** @@ -521,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(); } /** @@ -532,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); @@ -586,6 +872,11 @@ public final boolean exists(final F f) { return isSome() && f.f(some()); } + @Override + public final boolean equals(Object other) { + return Equal.equals0(Option.class, this, other, () -> Equal.optionEqual(Equal.anyEqual())); + } + /** * Projects an immutable collection of this optional value. * @@ -599,68 +890,23 @@ private static final class None extends Option { public A some() { throw error("some on None"); } - - @Override - public int hashCode() { - return 31; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - return true; - } } - private static final class Some extends Option { - private final A a; - - Some(final A a) { - this.a = a; - } + private static final class Some extends Option { + private final A a; - public A some() { - return a; - } + Some(final A a) { + this.a = a; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((a == null) ? 0 : a.hashCode()); - return result; + public A some() { + return a; + } } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Some other = (Some) obj; - if (a == null) { - if (other.a != null) - return false; - } else if (!a.equals(other.a)) - return false; - return true; - } - - } public static F> some_() { - return new F>() { - public Option f(final T t) { - return some(t); - } - }; + return Option::some; } /** @@ -670,15 +916,11 @@ public Option f(final T t) { * @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_() { - return new F>() { - public Option f(final T t) { - return none(); - } - }; + return t -> none(); } /** @@ -687,7 +929,7 @@ public Option f(final T t) { * @return An optional value that has no value. */ public static Option none() { - return new None(); + return new None<>(); } /** @@ -695,24 +937,32 @@ public static Option none() { * return none, otherwise, return the given value in some. * * @param t The unsafe nullable value. - * @return If t == null then return it in some, otherwise, return 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); } /** * Turns an unsafe nullable value into a safe optional value. If t == null then * return none, otherwise, return the given value in some. * - * @return If t == null then return it in some, otherwise, return none. + * @return If t == null then return none, otherwise, return it in some. */ public static F> fromNull() { - return new F>() { - public Option f(final T t) { - return 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); } /** @@ -727,19 +977,25 @@ 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()) : - a.head().bind(new F>>() { - public Option> f(final A aa) { - return sequence(a.tail()).map(cons_(aa)); - } - }); + 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()); } /** @@ -753,7 +1009,7 @@ public Option> f(final A aa) { * 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(); } /** @@ -765,8 +1021,8 @@ public static Option iif(final F f, final A a) { * @return An optional value that has a value of the given argument if the given boolean is true, otherwise, returns * no value. */ - public static Option iif(final boolean p, final P1 a) { - return p ? some(a._1()) : Option.none(); + public static Option iif(final boolean p, final F0 a) { + return p ? some(a.f()) : Option.none(); } /** @@ -789,11 +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 new F2, A, Option>() { - public Option f(final F p, final A a) { - return iif(p, a); - } - }; + return Option::iif; } /** @@ -803,11 +1055,7 @@ public Option f(final F p, final A a) { * @return All the values in the given list. */ public static List somes(final List> as) { - return as.filter(Option.isSome_()).map(new F, A>() { - public A f(final Option o) { - return o.some(); - } - }); + return as.filter(Option.isSome_()).map(o -> o.some()); } @@ -818,11 +1066,7 @@ public A f(final Option o) { * @return All the values in the given stream. */ public static Stream somes(final Stream> as) { - return as.filter(Option.isSome_()).map(new F, A>() { - public A f(final Option o) { - return o.some(); - } - }); + return as.filter(Option.isSome_()).map(o -> o.some()); } /** @@ -832,14 +1076,17 @@ public A f(final Option o) { * @return an optional non-empty string, or no value if the given string is empty. */ public static Option fromString(final String s) { - return fromNull(s).bind(new F>() { - public Option f(final String s) { - final Option none = none(); - return s.length() == 0 ? none : some(s); - } + return fromNull(s).bind(s1 -> { + final Option none = none(); + return s.length() == 0 ? none : some(s); }); } + @Override + public final int hashCode() { + return Hash.optionHash(Hash.anyHash()).hash(this); + } + /** * Returns a function that transforms a string to an optional non-empty string, * or no value if the string is empty. @@ -848,11 +1095,7 @@ public Option f(final String s) { * or no value if the string is empty. */ public static F> fromString() { - return new F>() { - public Option f(final String s) { - return fromString(s); - } - }; + return Option::fromString; } /** @@ -861,11 +1104,7 @@ public Option f(final String s) { * @return A function that takes an optional value to a value or errors if there is no value. */ public static F, A> fromSome() { - return new F, A>() { - public A f(final Option option) { - return option.some(); - } - }; + return option -> option.some(); } /** @@ -875,24 +1114,26 @@ public A f(final Option option) { * @return The given function promoted to operate on options. */ public static F, F, Option>> liftM2(final F> f) { - return curry(new F2, Option, Option>() { - public Option f(final Option a, final Option b) { - return a.bind(b, f); - } - }); + return curry((a, b) -> a.bind(b, f)); } + /** + * Lift the function of arity-2 through options. + * + * @param f A function to lift. + * @return An optional result. + */ + public final Option liftM2(final Option ob, final F2 f) { + return bind(a -> ob.map(b -> f.f(a, b))); + } + /** * First-class bind function. * * @return A function that binds a given function across an option with a final join. */ public static F>, F, Option>> bind() { - return curry(new F2>, Option, Option>() { - public Option f(final F> f, final Option a) { - return a.bind(f); - } - }); + return curry((f, a) -> a.bind(f)); } /** @@ -901,64 +1142,66 @@ public Option f(final F> f, final Option a) { * @return A function that joins an Option of an Option to make a single Option. */ public static F>, Option> join() { - return new F>, Option>() { - public Option f(final Option> option) { - return join(option); - } - }; + return Option::join; } /** * A function that parses a string to a byte. */ - public static final F> parseByte = new F>() { - public Option f(final String s) { - return parseByte(s).toOption(); - } - }; + public static final F> parseByte = s -> parseByte(s).toOption(); /** * A function that parses a string to a double. */ - public static final F> parseDouble = new F>() { - public Option f(final String s) { - return parseDouble(s).toOption(); - } - }; + public static final F> parseDouble = s -> parseDouble(s).toOption(); /** * A function that parses a string to a float. */ - public static final F> parseFloat = new F>() { - public Option f(final String s) { - return parseFloat(s).toOption(); - } - }; + public static final F> parseFloat = s -> parseFloat(s).toOption(); /** * A function that parses a string to an integer. */ - public static final F> parseInt = new F>() { - public Option f(final String s) { - return parseInt(s).toOption(); - } - }; + public static final F> parseInt = s -> parseInt(s).toOption(); /** * A function that parses a string to a long. */ - public static final F> parseLong = new F>() { - public Option f(final String s) { - return parseLong(s).toOption(); - } - }; + public static final F> parseLong = s -> parseLong(s).toOption(); /** * A function that parses a string to a short. */ - public static final F> parseShort = new F>() { - public Option f(final String s) { - return parseShort(s).toOption(); - } - }; + 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 f8210314..49010e31 100644 --- a/core/src/main/java/fj/data/Reader.java +++ b/core/src/main/java/fj/data/Reader.java @@ -1,44 +1,47 @@ package fj.data; import fj.F; -import fj.F1Functions; /** - * Created by MarkPerry on 7/07/2014. + * The Reader monad (also called the function monad, so equivalent to the idea of F). */ public class Reader { - private F function; + private final F function; public Reader(F f) { function = f; } + 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 ec48086b..59a46df7 100644 --- a/core/src/main/java/fj/data/Seq.java +++ b/core/src/main/java/fj/data/Seq.java @@ -1,24 +1,33 @@ package fj.data; -import fj.F; -import fj.F2; -import fj.F2Functions; -import fj.Function; +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.fingertrees.FingerTree; -import fj.data.fingertrees.MakeTree; -import fj.data.fingertrees.Measured; - /** * 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. */ -public final class Seq { +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()); + + @SuppressWarnings("unchecked") private static MakeTree mkTree() { - return FingerTree.mkTree(Seq.elemMeasured()); + return (MakeTree) MK_TREE; } private final FingerTree ftree; @@ -27,8 +36,9 @@ private Seq(final FingerTree ftree) { this.ftree = ftree; } + @SuppressWarnings("unchecked") private static Measured elemMeasured() { - return measured(intAdditionMonoid, Function.constant(1)); + return (Measured) ELEM_MEASURED; } /** @@ -36,8 +46,14 @@ private static Measured elemMeasured() { * * @return A sequence with no elements. */ + @SuppressWarnings("unchecked") public static Seq empty() { - return new Seq(Seq.mkTree().empty()); + return (Seq) EMPTY; + } + + @Override + public boolean equals(Object other) { + return Equal.equals0(Seq.class, this, other, () -> Equal.seqEqual(Equal.anyEqual())); } /** @@ -47,7 +63,65 @@ public static Seq empty() { * @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)); + } + + /** + * Constructs a sequence from the given elements. + * @param as The elements to create the sequence from. + * @return A sequence with the given elements. + */ + @SafeVarargs public static Seq seq(final A... as) { + return arraySeq(as); + } + + /** + * Constructs a sequence from the given list. + * + * @param list The list to create the sequence from. + * @return A sequence with the elements of the list. + */ + public static Seq listSeq(final List list) { + return iterableSeq(list); + } + + /** + * Constructs a sequence from the iterable. + * @param i The iterable to create the sequence from. + * @return A sequence with the elements of the iterable. + */ + public static Seq iterableSeq(final Iterable i) { + Seq s = empty(); + for (final A a: i) { + s = s.snoc(a); + } + return s; + } + + /** + * Constructs a sequence from the iterator. + * @param i The iterator to create the sequence from. + * @return A sequence with the elements of the iterator. + */ + public static Seq iteratorSeq(final Iterator i) { + return iterableSeq(() -> i); + } + + /** + * Constructs a sequence from the array. + */ + @SafeVarargs + public static Seq arraySeq(A... as) { + return iterableSeq(Array.array(as)); + } + + /** + * Constructs a sequence from the given list. + * @param list The list to create the sequence from. + * @return A sequence with the elements of the list. + */ + public static Seq fromJavaList(final java.util.List list) { + return iterableSeq(list); } /** @@ -57,7 +131,7 @@ public static Seq single(final A a) { * @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)); } /** @@ -67,7 +141,104 @@ 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)); + } + + /** + * The first element of this sequence. This is an O(1) operation. + * + * @return The first element if this sequence is nonempty, otherwise throws an error. + */ + public A head() { return ftree.head(); } + + public Option headOption() { + return ftree.headOption(); + } + + /** + * The last element of this sequence. This is an O(1) operation. + * + * @return The last element if this sequence is nonempty, otherwise throws an error. + */ + public A last() { return ftree.last(); } + + /** + * The sequence without the first element. This is an O(1) operation. + * + * @return The sequence without the first element if this sequence is nonempty, otherwise throws an error. + */ + public Seq tail() { + return (length() == 1) ? empty() : new Seq<>(ftree.tail()); + } + + /** + * The sequence without the last element. This is an O(1) operation. + * + * @return The sequence without the last element if this sequence is nonempty, otherwise throws an error. + */ + public Seq init() { + return (length() == 1) ? empty() : new Seq<>(ftree.init()); + } + + /** + * Converts this sequence to a Stream + */ + public Stream toStream() { + return ftree.foldLeft((b, a) -> b.cons(a), Stream.nil()).reverse(); + } + + /** + * Converts this sequence to a List + */ + public List toList() { + final Buffer buf = Buffer.empty(); + for (final A a : this) { buf.snoc(a); } + return buf.toList(); + } + + /** + * Converts the sequence to a java.util.List + */ + 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(); } + @Override public int size() { return length(); } + }; + } + + /** + * Returns an iterator for this seq. This method exists to permit the use in a for-each loop. + * + * @return A iterator for this seq. + */ + public Iterator iterator() { + return new Iterator() { + private FingerTree ftree = Seq.this.ftree; + + public boolean hasNext() { + return !ftree.isEmpty(); + } + + public A next() { + if (ftree.isEmpty()) + throw new NoSuchElementException(); + else { + final A a = ftree.head(); + ftree = ftree.tail(); + return a; + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public String toString() { + return Show.seqShow(Show.anyShow()).showS(this); } /** @@ -77,7 +248,7 @@ public Seq snoc(final A a) { * @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)); } /** @@ -89,6 +260,27 @@ 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. + * + * @return True if this sequence is not empty, otherwise false. + */ + public boolean isNotEmpty() { + return !ftree.isEmpty(); + } + /** * Returns the number of elements in this sequence. * @@ -99,17 +291,74 @@ public int length() { } /** - * Returns the element at the given index. + * Splits this sequence into a pair of sequences at the given position. This is a O(log(n)) operation. + * + * @return Pair: the subsequence containing elements with indices less than i + * and the subsequence containing elements with indices greater than or equal to i. + */ + public P2, Seq> split(final int i) { + final P2, FingerTree> lr = ftree.split(index -> index > i); + return P.p(new Seq<>(lr._1()), new Seq<>(lr._2())); + } + + /** + * Returns the element at the given index. This is an O(log(n)) operation. * * @param i The index of the element to return. * @return The element at the given index, or throws an error if the index is out of bounds. */ public A index(final int i) { - if (i < 0 || i >= length()) - throw error("Index " + i + "out of bounds."); - return ftree.lookup(Function.identity(), i)._2(); + checkBounds(i); + return ftree.lookup(Function.identity(), i)._2(); + } + + /** + * Replace the element at the given index with the supplied value. This is an O(log(n)) operation. + * + * @param i The index of the element to update. + * @param a The new value. + * + * @return The updated sequence, or throws an error if the index is out of bounds. + */ + public Seq update(final int i, final A a) { + checkBounds(i); + final P3, A, FingerTree> lxr = ftree.split1(index -> index > i); + return new Seq<>(lxr._1().append(lxr._3().cons(a))); + } + + /** + * Delete the element at the given index. This is an O(log(n)) operation. + * + * @param i The index of the element to update. + * + * @return The updated sequence, or throws an error if the index is out of bounds. + */ + public Seq delete(final int i) { + checkBounds(i); + final P3, A, FingerTree> lxr = ftree.split1(index -> index > i); + return new Seq<>(lxr._1().append(lxr._3())); } + /** + * Takes the given number of elements from the head of this sequence if they are available. + * + * @param n The maximum number of elements to take from this sequence. + * @return A sequence consisting only of the first n elements of this sequence, or else the whole sequence, + * if it has less than n elements. + */ + public Seq take(final int n) { return split(n)._1(); } + + /** + * Drops the given number of elements from the head of this sequence if they are available. + * + * @param n The number of elements to drop from this sequence. + * @return A sequence consisting of all elements of this sequence except the first n ones, or else the empty sequence, + * if this sequence has less than n elements. + */ + public Seq drop(final int n) { return split(n)._2(); } + + private void checkBounds(final int i) { if (i < 0 || i >= length()) throw error("Index " + i + " is out of bounds."); } + public B foldLeft(final F2 f, final B z) { return ftree.foldLeft(f, z); } @@ -118,8 +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 56a27ba4..4c0aeaa0 100644 --- a/core/src/main/java/fj/data/Set.java +++ b/core/src/main/java/fj/data/Set.java @@ -1,19 +1,13 @@ package fj.data; -import fj.F; -import fj.F2; -import fj.Function; -import fj.Monoid; -import fj.Ord; -import fj.P; -import fj.P2; -import fj.P3; +import fj.*; + import static fj.Function.*; import static fj.data.Either.right; +import static fj.data.Option.none; import static fj.data.Option.some; import static fj.function.Booleans.not; -import fj.Ordering; import static fj.Ordering.GT; import static fj.Ordering.LT; @@ -37,7 +31,7 @@ public final boolean isEmpty() { return this instanceof Empty; } - @SuppressWarnings({"ClassEscapesDefinedScope"}) + @SuppressWarnings("ClassEscapesDefinedScope") abstract Color color(); abstract Set l(); @@ -121,32 +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(new F>>() { - public P2> f(final A a2) { - return 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(new F>, P2>>() { - public P2> f(final P2> set) { - return 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(new F>, P2>>() { - public P2> f(final P2> set) { - return 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); } /** @@ -156,7 +138,22 @@ public P2> f(final P2> set) { * @return the empty set. */ public static Set empty(final Ord ord) { - return new Empty(ord); + return new Empty<>(ord); + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Set.class, this, other, () -> Equal.setEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.setHash(Hash.anyHash()).hash(this); + } + + @Override + public final String toString() { + return Show.setShow(Show.anyShow()).showS(this); } /** @@ -176,11 +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(new F2, A, Boolean>() { - public Boolean f(final Set s, final A a) { - return s.member(a); - } - }); + return curry(Set::member); } /** @@ -199,37 +192,33 @@ public final Set insert(final A x) { * @return A function that inserts a given element into a given set. */ public static F, Set>> insert() { - return curry(new F2, Set>() { - public Set f(final A a, final Set set) { - return set.insert(a); - } - }); + return curry((a, set) -> set.insert(a)); } 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() { @@ -277,8 +266,21 @@ public final Set map(final Ord o, final F f) { public final B foldMap(final F f, final Monoid m) { return isEmpty() ? m.zero() : - m.sum(m.sum(r().foldMap(f, m), f.f(head())), l().foldMap(f, m)); - } + m.sum(m.sum(l().foldMap(f, m), f.f(head())), r().foldMap(f, m)); + } + + /** + * Folds this Set from the right using the given monoid. + * + * @param f A transformation from this Set's elements, to the monoid. + * @param m The monoid to fold this Set with. + * @return The result of folding the Set from the right with the given monoid. + */ + public final B foldMapRight(final F f, final Monoid m) { + return isEmpty() ? + m.zero() : + m.sum(m.sum(r().foldMapRight(f, m), f.f(head())), l().foldMapRight(f, m)); + } /** * Returns a list representation of this set. @@ -286,19 +288,85 @@ public final B foldMap(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()); } /** - * Returns a stream representation of this set. + * Returns a java.util.Set representation of this set. * - * @return a stream representation of this set. + * @return a java.util.Set representation of this set. + */ + public final java.util.Set toJavaSet() { + return toJavaHashSet(); + } + + /** + * Returns a java.util.HashSet representation of this set. + * + * @return a java.util.HashSet representation of this set. */ - public final Stream toStream() { - return foldMap(Stream.single(), Monoid.streamMonoid()); + public final java.util.HashSet toJavaHashSet() { + return new java.util.HashSet<>(toStream().toCollection()); } /** + * Returns a java.util.TreeSet representation of this set. + * + * @return a java.util.TreeSet representation of this set. + */ + public final java.util.TreeSet toJavaTreeSet() { + return new java.util.TreeSet<>(toStream().toCollection()); + } + + /** + * Returns a java.util.List representation of this set. + * + * @return a java.util.List representation of this set. + */ + public final java.util.List toJavaList() { + return new java.util.ArrayList<>(toStream().toCollection()); + } + + /** + * Returns a list representation of this set in reverse order. + * + * @return a list representation of this set in reverse order. + */ + public final List toListReverse() { + return foldMapRight(List.cons(List.nil()), Monoid.listMonoid()); + } + + /** + * Returns a stream representation of this set. + * + * @return a stream representation of this set. + */ + public final Stream toStream() { + if (isEmpty()) { + return Stream.nil(); + } else if (l().isEmpty()) { + return Stream.cons(head(), () -> r().toStream()); + } else { + return l().toStream().append(Stream.cons(head(), () -> r().toStream())); + } + } + + /** + * Returns a stream representation of this set in reverse order. + * + * @return a stream representation of this set in reverse order. + */ + public final Stream toStreamReverse() { + if (isEmpty()) { + return Stream.nil(); + } else if (r().isEmpty()) { + return Stream.cons(head(), () -> l().toStreamReverse()); + } else { + return r().toStreamReverse().append(Stream.cons(head(), () -> l().toStreamReverse())); + } + } + + /** * Binds the given function across this set. * * @param o An order for the elements of the target set. @@ -326,11 +394,7 @@ public final Set union(final Set s) { * @see #union(Set) */ public static F, F, Set>> union() { - return curry(new F2, Set, Set>() { - public Set f(final Set s1, final Set s2) { - return s1.union(s2); - } - }); + return curry(Set::union); } /** @@ -360,11 +424,7 @@ public final Set delete(final A a) { * @return A function that deletes a given element from a given set. */ public final F, Set>> delete() { - return curry(new F2, Set>() { - public Set f(final A a, final Set set) { - return set.delete(a); - } - }); + return curry((a, set) -> set.delete(a)); } /** @@ -384,11 +444,7 @@ public final Set intersect(final Set s) { * @see #intersect(Set) */ public static F, F, Set>> intersect() { - return curry(new F2, Set, Set>() { - public Set f(final Set s1, final Set s2) { - return s1.intersect(s2); - } - }); + return curry(Set::intersect); } /** @@ -408,13 +464,17 @@ public final Set minus(final Set s) { * @see #minus(Set) */ public static F, F, Set>> minus() { - return curry(new F2, Set, Set>() { - public Set f(final Set s1, final Set s2) { - return s1.minus(s2); - } - }); + return curry(Set::minus); } + public final Option min() { + return isEmpty() ? none() : l().min().orElse(some(head())); + } + + public final Option max() { + return isEmpty() ? none() : r().max().orElse(some(head())); + } + /** * Returns the size of this set. * @@ -440,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); @@ -455,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. * @@ -479,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)); } /** @@ -496,6 +679,29 @@ public static Set iterableSet(final Ord o, final Iterable as) { return s; } + /** + * Return the elements of the given iterator as a set. + * + * @param o An order for the elements of the new set. + * @param as An iterator of elements to add to a set. + * @return A new set containing the elements of the given iterator. + */ + public static Set iteratorSet(final Ord o, final Iterator as) { + return iterableSet(o, () -> as); + } + + /** + * Return the elements of the given iterator as a set. + * + * @param o An order for the elements of the new set. + * @param as An iterator of elements to add to a set. + * @return A new set containing the elements of the given iterator. + */ + @SafeVarargs + public static Set arraySet(final Ord o, final A...as) { + return iterableSet(o, Array.array(as)); + } + /** * Constructs a set from the given elements. * @@ -503,11 +709,8 @@ public static Set iterableSet(final Ord o, final Iterable as) { * @param as The elements to add to a set. * @return A new set containing the elements of the given iterable. */ - public static Set set(final Ord o, final A ... as) { - Set s = empty(o); - for (final A a : as) - s = s.insert(a); - return s; + @SafeVarargs public static Set set(final Ord o, final A ... as) { + return arraySet(o, as); } } 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 44bac66a..8afa36ee 100644 --- a/core/src/main/java/fj/data/Stream.java +++ b/core/src/main/java/fj/data/Stream.java @@ -1,10 +1,13 @@ package fj.data; -import fj.Effect; import fj.Equal; +import fj.F0; +import fj.F3; +import fj.Hash; +import fj.Semigroup; +import fj.Show; import fj.F; import fj.F2; -import fj.F3; import fj.Function; import fj.Monoid; import fj.Ord; @@ -12,24 +15,19 @@ 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; import fj.function.Effect1; -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; +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; @@ -42,8 +40,6 @@ /** * A lazy (not yet evaluated), immutable, singly linked list. - * - * @version %build.number% */ public abstract class Stream implements Iterable { private Stream() { @@ -98,7 +94,7 @@ public final boolean isNotEmpty() { * @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. */ - public final B stream(final B nil, final F>, B>> cons) { + public final B uncons(final B nil, final F>, B>> cons) { return isEmpty() ? nil : cons.f(head()).f(tail()); } @@ -110,11 +106,7 @@ public final B stream(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(new P1() { - public B _1() { - return tail()._1().foldRight(f, b); - } - }); + return foldRight(uncurryF2(f), b); } /** @@ -125,7 +117,7 @@ public B _1() { * @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))); } /** @@ -136,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); } /** @@ -147,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)); } /** @@ -158,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); } /** @@ -174,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; } /** @@ -185,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()); } /** @@ -196,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)); } /** @@ -207,8 +199,8 @@ public final A foldLeft1(final F> f) { * @param a The argument to return if this stream is empty. * @return The head of this stream if there is one or the given argument if this stream is empty. */ - public final A orHead(final P1 a) { - return isEmpty() ? a._1() : head(); + public final A orHead(final F0 a) { + return isEmpty() ? a.f() : head(); } /** @@ -217,8 +209,8 @@ public final A orHead(final P1 a) { * @param as The argument to return if this stream is empty. * @return The tail of this stream if there is one or the given argument if this stream is empty. */ - public final P1> orTail(final P1> as) { - return isEmpty() ? as : tail(); + public final P1> orTail(final F0> as) { + return isEmpty() ? P.lazy(as) : tail(); } /** @@ -234,11 +226,7 @@ public Stream _1() { } public Stream prefix(final A x, final Stream xs) { - return xs.isEmpty() ? xs : cons(x, p(cons(xs.head(), new P1>() { - public Stream _1() { - return prefix(a, xs.tail()._1()); - } - }))); + return xs.isEmpty() ? xs : cons(x, p(cons(xs.head(), () -> prefix(a, xs.tail()._1())))); } }); } @@ -250,11 +238,7 @@ public Stream _1() { * @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()), new P1>() { - public Stream _1() { - return tail()._1().map(f); - } - }); + return isEmpty() ? Stream.nil() : cons(f.f(head()), () -> tail()._1().map(f)); } /** @@ -263,15 +247,7 @@ public Stream _1() { * @return A function that maps a given function across a given stream. */ public static F, F, Stream>> map_() { - return new F, F, Stream>>() { - public F, Stream> f(final F f) { - return new F, Stream>() { - public Stream f(final Stream as) { - return as.map(f); - } - }; - } - }; + return f -> as -> as.map(f); } /** @@ -292,7 +268,7 @@ public final Unit foreach(final F f) { * * @param f The side-effect to perform for the given element. */ - public final void foreach(final Effect1 f) { + public final void foreachDoEffect(final Effect1 f) { for (Stream xs = this; xs.isNotEmpty(); xs = xs.tail()._1()) f.f(xs.head()); } @@ -306,11 +282,7 @@ public final void foreach(final Effect1 f) { */ public final Stream filter(final F f) { final Stream as = dropWhile(not(f)); - return as.isNotEmpty() ? cons(as.head(), new P1>() { - public Stream _1() { - return as.tail()._1().filter(f); - } - }) : as; + return as.isNotEmpty() ? cons(as.head(), () -> as.tail()._1().filter(f)) : as; } /** @@ -320,11 +292,7 @@ public Stream _1() { * @return A new stream that has appended the given stream. */ public final Stream append(final Stream as) { - return isEmpty() ? as : cons(head(), new P1>() { - public Stream _1() { - return tail()._1().append(as); - } - }); + return isEmpty() ? as : cons(head(), () -> tail()._1().append(as)); } /** @@ -333,12 +301,8 @@ public Stream _1() { * @param as The stream to append to this one. * @return A new stream that has appended the given stream. */ - public final Stream append(final P1> as) { - return isEmpty() ? as._1() : cons(head(), new P1>() { - public Stream _1() { - return tail()._1().append(as); - } - }); + public final Stream append(final F0> as) { + return isEmpty() ? as.f() : cons(head(), () -> tail()._1().append(as)); } /** @@ -371,16 +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(new F2, P1>>, F>>() { - public F> f(final F baf, final P1>> p1) { - return Function.bind(baf, p1._1(), Function.curry(new F2, Stream>() { - public Stream f(final A a, final Stream stream) { - return 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())); } /** @@ -402,16 +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 map(f).foldLeft(new F2, Stream, Stream>() { - @Override - public Stream f(Stream accumulator, Stream element) { - Stream result = accumulator; - for (B single : element) { - result = result.cons(single); - } - return result; - } - }, Stream.nil()).reverse(); + return foldRight(h -> t -> f.f(h).append(t), nil()); } /** @@ -551,6 +498,36 @@ public final Stream sequence(final Stream bs) { return bind(c); } + /** + * Sequence through the Stream monad. + * + * @param io The IO stream to sequence. + * @return The stream of IOs after sequencing. + */ + public static Stream> sequence(final IO> io) { + return IOFunctions.runSafe(io).map(IOFunctions::unit); + } + + /** + * Sequence through the Stream monad. + * + * @param p The lazy stream to sequence. + * @return The stream of (pre-calculated) lazy values after sequencing. + */ + public static Stream> sequence(final F0> p) { + return p.f().map(P::p); + } + + /** + * Sequence through the Stream monad. + * + * @param o The optional stream to sequence. + * @return The stream of options after sequencing. + */ + public static Stream> sequence(final Option> o) { + return o.isNone() ? nil() : o.some().map(Option::some); + } + /** * Performs function application within a stream (applicative functor pattern). * @@ -558,15 +535,7 @@ public final Stream sequence(final Stream bs) { * @return A new stream after applying the given stream of functions through this stream. */ public final Stream apply(final Stream> sf) { - return sf.bind(new F, Stream>() { - public Stream f(final F f) { - return map(new F() { - public B f(final A a) { - return f.f(a); - } - }); - } - }); + return sf.bind(this::map); } /** @@ -576,11 +545,15 @@ public B f(final A a) { * @return A new stream with elements interleaved from this stream and the given stream. */ public final Stream interleave(final Stream as) { - return isEmpty() ? as : as.isEmpty() ? this : cons(head(), new P1>() { - @Override public Stream _1() { - return as.interleave(tail()._1()); - } - }); + return isEmpty() ? as : as.isEmpty() ? this : cons(head(), () -> as.interleave(tail()._1())); + } + + public static Stream enumerationStream(Enumeration e) { + if (e.hasMoreElements()) { + return cons(e.nextElement(), () -> enumerationStream(e)); + } else { + return nil(); + } } /** @@ -590,7 +563,7 @@ public final Stream interleave(final Stream as) { * @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. @@ -608,11 +581,7 @@ private static Stream> mergePairs(final Ord o, final Stream> t = s.tail()._1(); - return cons(merge(o, s.head(), t.head()), new P1>>() { - public Stream> _1() { - return mergePairs(o, t.tail()._1()); - } - }); + return cons(merge(o, s.head(), t.head()), () -> mergePairs(o, t.tail()._1())); } // Merges two individually sorted streams. @@ -624,16 +593,8 @@ private static Stream merge(final Ord o, final Stream xs, final Str final A x = xs.head(); final A y = ys.head(); if (o.isGreaterThan(x, y)) - return cons(y, new P1>() { - public Stream _1() { - return merge(o, xs, ys.tail()._1()); - } - }); - return cons(x, new P1>() { - public Stream _1() { - return merge(o, xs.tail()._1(), ys); - } - }); + return cons(y, () -> merge(o, xs, ys.tail()._1())); + return cons(x, () -> merge(o, xs.tail()._1(), ys)); } /** @@ -650,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(); @@ -663,11 +624,7 @@ private Promise> qs(final Ord o, final Strategy s) { } private static F, Promise>> qs_(final Ord o, final Strategy s) { - return new F, Promise>>() { - public Promise> f(final Stream xs) { - return xs.qs(o, s); - } - }; + return xs -> xs.qs(o, s); } private static F, Promise>> flt(final Ord o, @@ -726,11 +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, new P1>() { - public Stream _1() { - return range(from + 1, to); - } - }); + return from >= to ? Stream.nil() : cons(from, () -> range(from + 1, to)); } /** @@ -739,14 +692,19 @@ public Stream _1() { * @param as The elements which which to construct a stream. * @return a new stream with the given elements. */ - public static Stream stream(final A... as) { - return as.length == 0 ? Stream.nil() - : unfold(P2.tuple(new F2>>>() { - public Option>> f(final A[] as, final Integer i) { - return i >= as.length ? Option.>>none() - : some(P.p(as[i], P.p(as, i + 1))); - } - }), P.p(as, 0)); + @SafeVarargs public static Stream stream(final A... as) { + return arrayStream(as); + } + + /** + * Constructs a stream with the given elements in the Iterator. + */ + public static Stream iteratorStream(final Iterator it) { + if (it.hasNext()) { + final A a = it.next(); + return cons(a, () -> iteratorStream(it)); + } else + return nil(); } /** @@ -773,15 +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, new P1>() { - public Stream _1() { - return e.plus(from, step).map(new F>() { - public Stream f(final A a) { - return forever(e, a, step); - } - }).orSome(Stream.nil()); - } - }); + return cons(from, () -> e.plus(from, step).map(a -> forever(e, a, step)).orSome(Stream.nil())); } /** @@ -811,21 +761,10 @@ 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, new P1>() { - public Stream _1() { - return Stream.join(e.plus(from, step).filter(new F() { - public Boolean f(final A a) { - return !(o == LT ? e.order().isLessThan(to, a) : e.order().isGreaterThan(to, a)); - } - }).map(new F>() { - public Stream f(final A a) { - return range(e, a, to, step); - } - }).toStream()); - } - }); + 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())); } - /** * Returns an infinite stream of integers from the given from value (inclusive). * @@ -833,11 +772,7 @@ public Stream f(final A a) { * @return A stream of integers from the given from value (inclusive). */ public static Stream range(final int from) { - return cons(from, new P1>() { - public Stream _1() { - return range(from + 1); - } - }); + return cons(from, () -> range(from + 1)); } /** @@ -846,11 +781,7 @@ public Stream _1() { * @return a function that filters a given stream using a given predicate. */ public static F, F, Stream>> filter() { - return curry(new F2, Stream, Stream>() { - public Stream f(final F f, final Stream as) { - return as.filter(f); - } - }); + return curry((f, as) -> as.filter(f)); } /** @@ -862,12 +793,8 @@ public Stream f(final F f, final Stream as) { * @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() : - cons(fs.head().f(head()), new P1>() { - public Stream _1() { - return tail()._1().zapp(fs.tail()._1()); - } - }); + return fs.isEmpty() || isEmpty() ? Stream.nil() : + cons(fs.head().f(head()), () -> tail()._1().zapp(fs.tail()._1())); } /** @@ -906,11 +833,7 @@ public final Stream zipWith(final Stream bs, final F2 f) { * @return A function that zips a given stream with this stream using the given function. */ public final F, Stream> zipWith(final F> f) { - return new F, Stream>() { - public Stream f(final Stream stream) { - return zipWith(stream, f); - } - }; + return stream -> zipWith(stream, f); } /** @@ -933,11 +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), new F2>() { - public P2 f(final A a, final Integer i) { - return p(a, i); - } - }); + return zipWith(range(0), (F2>) P::p); } /** @@ -947,8 +866,8 @@ public P2 f(final A a, final Integer i) { * @param x The value to return in left if this stream is empty. * @return An either projection of this stream. */ - public final Either toEither(final P1 x) { - return isEmpty() ? Either.left(x._1()) : Either.right(head()); + public final Either toEither(final F0 x) { + return isEmpty() ? Either.left(x.f()) : Either.right(head()); } /** @@ -958,7 +877,7 @@ public final Either toEither(final P1 x) { * @return An option projection of this stream. */ public final Option toOption() { - return isEmpty() ? Option.none() : some(head()); + return isEmpty() ? Option.none() : some(head()); } /** @@ -966,23 +885,25 @@ public final Option toOption() { * * @return A list projection of this stream. */ - public final List toList() { - List as = List.nil(); - - for (Stream x = this; !x.isEmpty(); x = x.tail()._1()) { - as = as.snoc(x.head()); + public final List toList() { + List.Buffer buf = List.Buffer.empty(); + foreachDoEffect(buf::snoc); + return buf.toList(); } - return as; + /** + * Returns a java.util.List projection of this stream. + */ + public final java.util.List toJavaList() { + return new java.util.LinkedList<>(toCollection()); } - /** * Returns a array projection of this stream. * * @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]; @@ -1031,11 +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, new P1>() { - public Stream _1() { - return Stream.this; - } - }); + return new Cons<>(a, () -> Stream.this); } /** @@ -1046,7 +963,9 @@ public Stream _1() { * @return A string from the given stream of characters. */ public static String asString(final Stream cs) { - return LazyString.fromStream(cs).toString(); + StringBuilder sb = new StringBuilder(); + cs.foreachDoEffect(sb::append); + return sb.toString(); } /** @@ -1076,12 +995,8 @@ public final Stream snoc(final A a) { * @param a The element to append. * @return A new stream with the given element at the end. */ - public final Stream snoc(final P1 a) { - return append(new P1>() { - public Stream _1() { - return single(a._1()); - } - }); + public final Stream snoc(final F0 a) { + return append(() -> single(a.f())); } /** @@ -1092,12 +1007,8 @@ public Stream _1() { */ public final Stream take(final int n) { return n <= 0 || isEmpty() ? - Stream.nil() : - cons(head(), new P1>() { - public Stream _1() { - return tail()._1().take(n - 1); - } - }); + Stream.nil() : + cons(head(), () -> n <= 1 ? Stream.nil() : tail()._1().take(n - 1)); } /** @@ -1107,11 +1018,9 @@ public Stream _1() { * @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; @@ -1128,12 +1037,8 @@ public final Stream takeWhile(final F f) { return isEmpty() ? this : f.f(head()) ? - cons(head(), new P1>() { - public Stream _1() { - return tail()._1().takeWhile(f); - } - }) : - Stream.nil(); + cons(head(), () -> tail()._1().takeWhile(f)) : + Stream.nil(); } /** @@ -1163,22 +1068,13 @@ public final P2, Stream> span(final F p) { if (isEmpty()) return p(this, this); else if (p.f(head())) { - final P1, Stream>> yszs = new P1, Stream>>() { - @Override public P2, Stream> _1() { - return tail()._1().span(p); - } - }; - return new P2, Stream>() { - @Override public Stream _1() { - return cons(head(), yszs.map(P2., Stream>__1())); - } - - @Override public Stream _2() { - return yszs._1()._2(); - } - }; + final P1, Stream>> yszs = P.lazy(() -> tail()._1().span(p)); + return P.lazy( + () -> cons(head(), yszs.map(P2.__1())), + () -> yszs._1()._2() + ); } else - return p(Stream.nil(), this); + return p(Stream.nil(), this); } /** @@ -1193,11 +1089,7 @@ public final Stream replace(final F p, final A a) { return nil(); else { final P2, Stream> s = span(p); - return s._1().append(cons(a, new P1>() { - @Override public Stream _1() { - return s._2().tail()._1().replace(p, a); - } - })); + return s._1().append(cons(a, () -> s._2().tail()._1().replace(p, a))); } } @@ -1219,19 +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(new F, F>>() { - public F> f(final Stream as) { - return new F>() { - public Stream f(final A a) { - return cons(a, new P1>() { - public Stream _1() { - return as; - } - }); - } - }; - } - }, Stream.nil()); + return foldLeft((as, a) -> cons(a, () -> as), Stream.nil()); } /** @@ -1295,7 +1175,33 @@ public final A index(final int i) { * false otherwise. */ public final boolean forall(final F f) { - return isEmpty() || f.f(head()) && tail()._1().forall(f); + for (final A a : this) { + if (!f.f(a)) return false; + } + return true; + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Stream.class, this, other, () -> Equal.streamEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.streamHash(Hash.anyHash()).hash(this); + } + + @Override + public final String toString() { + return toStringLazy(); + } + + public final String toStringLazy() { + return isEmpty() ? "Nil()" : "Cons(" + Show.anyShow().showS(head()) + ", ?)"; + } + + public final String toStringEager() { + return Show.streamShow(Show.anyShow()).showS(this); } /** @@ -1343,11 +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, new P1>>() { - public Stream> _1() { - return tail()._1().tails(); - } - }); + return isEmpty() ? Stream.nil() : cons(this, () -> tail()._1().tails()); } /** @@ -1356,16 +1258,8 @@ public Stream> _1() { * @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(), new P1>>() { - public Stream> _1() { - return nil(); - } - }); - return isEmpty() ? nil : nil.append(new P1>>() { - public Stream> _1() { - return tail()._1().inits().map(Stream.cons_().f(head())); - } - }); + final Stream> nil = cons(Stream.nil(), Stream::nil); + return isEmpty() ? nil : nil.append(() -> tail()._1().inits().map(Stream.cons_().f(head()))); } /** @@ -1374,11 +1268,7 @@ public Stream> _1() { * @return a stream of the infixes of this stream. */ public final Stream> substreams() { - return tails().bind(new F, Stream>>() { - public Stream> f(final Stream stream) { - return stream.inits(); - } - }); + return tails().bind(Stream::inits); } /** @@ -1388,11 +1278,7 @@ public Stream> f(final Stream stream) { * @return the position of the first element matching the given predicate, if any. */ public final Option indexOf(final F p) { - return zipIndex().find(new F, Boolean>() { - public Boolean f(final P2 p2) { - return p.f(p2._1()); - } - }).map(P2.__2()); + return zipIndex().find(p2 -> p.f(p2._1())).map(P2.__2()); } /** @@ -1403,12 +1289,8 @@ public Boolean f(final P2 p2) { */ public final Stream sequenceW(final Stream, B>> fs) { return fs.isEmpty() - ? Stream.nil() - : cons(fs.head().f(this), new P1>() { - public Stream _1() { - return sequenceW(fs.tail()._1()); - } - }); + ? Stream.nil() + : cons(fs.head().f(this), () -> sequenceW(fs.tail()._1())); } /** @@ -1417,11 +1299,7 @@ public Stream _1() { * @return A function from natural numbers to values with the corresponding position in this stream. */ public final F toFunction() { - return new F() { - public A f(final Integer i) { - return index(i); - } - }; + return this::index; } /** @@ -1445,14 +1323,12 @@ public static Stream fromFunction(final F f) { * starting at the given value. */ public static Stream fromFunction(final Enumerator e, final F f, final B i) { - return cons(f.f(i), new P1>() { - public Stream _1() { + return cons(f.f(i), () -> { final Option s = e.successor(i); return s.isSome() ? fromFunction(e, f, s.some()) - : Stream.nil(); - } - }); + : Stream.nil(); + }); } /** @@ -1462,12 +1338,10 @@ public Stream _1() { * @return A stream of first components and a stream of second components. */ public static P2, Stream> unzip(final Stream> xs) { - return xs.foldRight(new F2, P1, Stream>>, P2, Stream>>() { - public P2, Stream> f(final P2 p, final P1, Stream>> 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 xs.foldRight((p, ps) -> { + final P2, Stream> pp = ps._1(); + return p(cons(p._1(), p(pp._1())), cons(p._2(), p(pp._2()))); + }, p(Stream.nil(), Stream.nil())); } /** @@ -1476,11 +1350,7 @@ public P2, Stream> f(final P2 p, final P1, Strea * @return a function that zips two given streams with a given function. */ public static F, F, F>, Stream>>> zipWith() { - return curry(new F3, Stream, F>, Stream>() { - public Stream f(final Stream as, final Stream bs, final F> f) { - return as.zipWith(bs, f); - } - }); + return curry((F3, Stream, F>, Stream>) Stream::zipWith); } private static final class Nil extends Stream { @@ -1497,9 +1367,9 @@ private static final class Cons extends Stream { private final A head; private final P1> tail; - Cons(final A head, final P1> tail) { + Cons(final A head, final F0> tail) { this.head = head; - this.tail = tail.memo(); + this.tail = weakMemo(tail); } public A head() { @@ -1518,15 +1388,7 @@ public P1> tail() { * @return A function that prepends (cons) an element to a stream to produce a new stream. */ public static F>, Stream>> cons() { - return new F>, Stream>>() { - public F>, Stream> f(final A a) { - return new F>, Stream>() { - public Stream f(final P1> list) { - return cons(a, list); - } - }; - } - }; + return a -> list -> cons(a, list); } /** @@ -1535,11 +1397,7 @@ public Stream f(final P1> list) { * @return A function that prepends (cons) an element to a stream to produce a new stream. */ public static F, Stream>> cons_() { - return curry(new F2, Stream>() { - public Stream f(final A a, final Stream as) { - return as.cons(a); - } - }); + return curry((a, as) -> as.cons(a)); } /** @@ -1548,7 +1406,7 @@ public Stream f(final A a, final Stream as) { * @return An empty stream. */ public static Stream nil() { - return new Nil(); + return new Nil<>(); } /** @@ -1557,11 +1415,7 @@ public static Stream nil() { * @return An empty stream. */ public static P1> nil_() { - return new P1>() { - public Stream _1() { - return new Nil(); - } - }; + return P.lazy(Nil::new); } /** @@ -1570,11 +1424,7 @@ public Stream _1() { * @return A function that determines whether a given stream is empty. */ public static F, Boolean> isEmpty_() { - return new F, Boolean>() { - public Boolean f(final Stream as) { - return as.isEmpty(); - } - }; + return Stream::isEmpty; } /** @@ -1583,11 +1433,7 @@ public Boolean f(final Stream as) { * @return A function that determines whether a given stream is not empty. */ public static F, Boolean> isNotEmpty_() { - return new F, Boolean>() { - public Boolean f(final Stream as) { - return as.isNotEmpty(); - } - }; + return Stream::isNotEmpty; } /** @@ -1597,11 +1443,7 @@ public Boolean f(final Stream as) { * @return A stream of one element containing the given value. */ public static Stream single(final A a) { - return cons(a, new P1>() { - public Stream _1() { - return nil(); - } - }); + return cons(a, Stream::nil); } /** @@ -1610,11 +1452,7 @@ public Stream _1() { * @return a function that yields a stream containing its argument. */ public static F> single() { - return new F>() { - public Stream f(final A a) { - return single(a); - } - }; + return Stream::single; } /** @@ -1624,8 +1462,8 @@ public Stream f(final A a) { * @param tail The stream to prepend to. * @return The stream with the given element prepended. */ - public static Stream cons(final A head, final P1> tail) { - return new Cons(head, tail); + public static Stream cons(final A head, final F0> tail) { + return new Cons<>(head, tail); } /** @@ -1634,9 +1472,7 @@ public static Stream cons(final A head, final P1> tail) { * @param o The stream of streams to join. * @return A new stream that is the join of the given streams. */ - public static Stream join(final Stream> o) { - return Monoid.streamMonoid().sumRight(o); - } + public static Stream join(final Stream> o) { return o.bind(identity()); } /** * A first-class version of join @@ -1644,11 +1480,7 @@ public static Stream join(final Stream> o) { * @return A function that joins a stream of streams using a bind operation. */ public static F>, Stream> join() { - return new F>, Stream>() { - public Stream f(final Stream> as) { - return join(as); - } - }; + return Stream::join; } /** @@ -1665,11 +1497,7 @@ public static Stream unfold(final F>> f, final B b) return nil(); else { final P2 p = o.some(); - return cons(p._1(), new P1>() { - public Stream _1() { - return unfold(f, p._2()); - } - }); + return cons(p._1(), () -> unfold(f, p._2())); } } @@ -1685,16 +1513,8 @@ public Stream _1() { */ public static Stream iterateWhile(final F f, final F p, final A a) { return unfold( - new F>>() { - public Option> f(final A o) { - return Option.iif(new F, Boolean>() { - public Boolean f(final P2 p2) { - return p.f(o); - } - }, P.p(o, f.f(o))); - } - } - , a); + o -> Option.iif(p2 -> p.f(o), p(o, f.f(o))) + , a); } /** @@ -1704,21 +1524,14 @@ public Boolean f(final P2 p2) { * @return A stream from the given iterable. */ public static Stream iterableStream(final Iterable i) { - final class Util { - public Stream iteratorStream(final Iterator i) { - if (i.hasNext()) { - final A a = i.next(); - return cons(a, new P1>() { - public Stream _1() { - return iteratorStream(i); - } - }); - } else - return nil(); - } - } + return iteratorStream(i.iterator()); + } - return new Util().iteratorStream(i.iterator()); + @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(as[i], p(as, i + 1)))), p(as, 0)); } /** @@ -1728,11 +1541,7 @@ public Stream _1() { * @return An infinite-length stream of the given element. */ public static Stream repeat(final A a) { - return cons(a, new P1>() { - public Stream _1() { - return repeat(a); - } - }); + return cons(a, () -> repeat(a)); } /** @@ -1745,11 +1554,7 @@ public static Stream cycle(final Stream as) { if (as.isEmpty()) throw error("cycle on empty list"); else - return as.append(new P1>() { - public Stream _1() { - return cycle(as); - } - }); + return as.append(() -> cycle(as)); } /** @@ -1760,11 +1565,7 @@ public Stream _1() { * @return A stream constructed by applying the given iteration function starting at the given value. */ public static Stream iterate(final F f, final A a) { - return cons(a, new P1>() { - public Stream _1() { - return iterate(f, f.f(a)); - } - }); + return cons(a, () -> iterate(f, f.f(a))); } /** @@ -1774,11 +1575,7 @@ public Stream _1() { * starting at a given value. */ public static F, F>> iterate() { - return curry(new F2, A, Stream>() { - public Stream f(final F f, final A a) { - return iterate(f, a); - } - }); + return curry(Stream::iterate); } /** @@ -1787,11 +1584,7 @@ public Stream f(final F f, final A a) { * @return A function that binds a given function across a given stream, joining the resulting streams. */ public static F>, F, Stream>> bind_() { - return curry(new F2>, Stream, Stream>() { - public Stream f(final F> f, final Stream as) { - return as.bind(f); - } - }); + return curry((f, as) -> as.bind(f)); } /** @@ -1800,10 +1593,415 @@ public Stream f(final F> f, final Stream as) { * @return A function that folds a given stream with a given function. */ public static F, B>>, F, B>>> foldRight() { - return curry(new F3, B>>, B, Stream, B>() { - public B f(final F, B>> f, final B b, final Stream as) { - return as.foldRight(f, b); - } - }); + 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 8c3cd8b1..3b12d506 100644 --- a/core/src/main/java/fj/data/Tree.java +++ b/core/src/main/java/fj/data/Tree.java @@ -1,23 +1,15 @@ package fj.data; -import fj.F; -import fj.F2; -import fj.F2Functions; -import fj.P; -import fj.P1; -import fj.P2; +import fj.*; + import static fj.Function.*; import static fj.data.Stream.*; -import fj.Monoid; -import fj.Show; import java.util.Collection; import java.util.Iterator; /** * Provides a lazy, immutable, non-empty, multi-way tree (a rose tree). - * - * @version %build.number% */ public final class Tree implements Iterable { /** @@ -44,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()); } /** @@ -55,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); } /** @@ -66,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)); } /** @@ -86,11 +78,7 @@ public static Tree node(final A root, final List> forest) { * @return A function that constructs an n-ary tree given a root and a subforest or length n. */ public static F>>, Tree>> node() { - return curry(new F2>>, Tree>() { - public Tree f(final A a, final P1>> p1) { - return node(a, p1); - } - }); + return curry((a, p1) -> node(a, p1)); } /** @@ -117,11 +105,7 @@ public P1>> subForest() { * @return A transformation from a tree to its root. */ public static F, A> root_() { - return new F, A>() { - public A f(final Tree a) { - return a.root(); - } - }; + return Tree::root; } /** @@ -130,11 +114,7 @@ public A f(final Tree a) { * @return A transformation from a tree to its subforest. */ public static F, P1>>> subForest_() { - return new F, P1>>>() { - public P1>> f(final Tree a) { - return a.subForest(); - } - }; + return Tree::subForest; } /** @@ -145,26 +125,24 @@ public P1>> f(final Tree a) { 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 new F, Stream>() { - public Stream f(final Tree t) { - return t.flatten(); - } - }; + return Tree::flatten; } /** @@ -174,9 +152,9 @@ public Stream f(final Tree t) { */ 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); } /** @@ -195,15 +173,7 @@ public Tree fmap(final F f) { * @return A transformation to lift any function so that it maps over Trees. */ public static F, F, Tree>> fmap_() { - return new F, F, Tree>>() { - public F, Tree> f(final F f) { - return new F, Tree>() { - public Tree f(final Tree a) { - return a.fmap(f); - } - }; - } - }; + return f -> a -> a.fmap(f); } /** @@ -234,11 +204,7 @@ public Collection toCollection() { * @return A function that, given a tree, folds it with the given monoid. */ public static F, B> foldMap_(final F f, final Monoid m) { - return new F, B>() { - public B f(final Tree t) { - return t.foldMap(f, m); - } - }; + return t -> t.foldMap(f, m); } /** @@ -248,11 +214,9 @@ public B f(final Tree t) { * @return A function which, given a seed value, yields a tree. */ public static F> unfoldTree(final F>>> f) { - return new F>() { - public Tree f(final B b) { - final P2>> p = f.f(b); - return node(p._1(), p._2().map(Stream.>map_().f(unfoldTree(f)))); - } + return b -> { + final P2>> p = f.f(b); + return node(p._1(), p._2().map(Stream.>map_().f(unfoldTree(f)))); }; } @@ -265,11 +229,7 @@ public Tree f(final B b) { * root's children are labels of the root's subforest, etc. */ public Tree cobind(final F, B> f) { - return unfoldTree(new F, P2>>>>() { - public P2>>> f(final Tree t) { - return P.p(f.f(t), t.subForest()); - } - }).f(this); + return unfoldTree((Tree t) -> P.p(f.f(t), t.subForest())).f(this); } /** @@ -285,20 +245,35 @@ 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) { return drawSubTrees(s, subForest._1()).cons(s.showS(root)); } + @Override + public boolean equals(Object other) { + return Equal.equals0(Tree.class, this, other, () -> Equal.treeEqual(Equal.anyEqual())); + } + + @Override + public int hashCode() { + return Hash.treeHash(Hash.anyHash()).hash(this); + } + + @Override + public String toString() { + return Show.treeShow(Show.anyShow()).showS(this); + } + /** * Draws a 2-dimensional representation of a tree. * @@ -316,11 +291,7 @@ public String draw(final Show s) { * @return a show instance that draws a 2-dimensional representation of a tree. */ public static Show> show2D(final Show s) { - return Show.showS(new F, String>() { - public String f(final Tree tree) { - return tree.draw(s); - } - }); + return Show.showS(tree -> tree.draw(s)); } /** @@ -332,7 +303,7 @@ public String f(final Tree tree) { * @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); } /** @@ -355,25 +326,24 @@ public Tree zipWith(final Tree bs, final F> f) { * @return The folded tree */ public static Tree bottomUp(Tree t, final F>, B> f) { - final F, Tree> recursiveCall = new F, Tree>() { - @Override public Tree f(Tree a) { - return bottomUp(a, 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 new F, A>() { - @Override public A f(Tree a) { - return a.root(); - } - }; + return Tree::root; } -} \ No newline at end of file + public boolean isLeaf() { + return subForest._1().isEmpty(); + } + + public int length() { + return 1 + subForest._1().map(Tree::length).foldLeft((acc, i) -> acc + i, 0); + } + +} diff --git a/core/src/main/java/fj/data/TreeMap.java b/core/src/main/java/fj/data/TreeMap.java index 636dce7b..e1af4d91 100644 --- a/core/src/main/java/fj/data/TreeMap.java +++ b/core/src/main/java/fj/data/TreeMap.java @@ -1,12 +1,15 @@ package fj.data; +import fj.Equal; import fj.F; -import fj.F1Functions; +import fj.Hash; +import fj.Ord; import fj.P; import fj.P2; import fj.P3; -import fj.Ord; +import fj.Show; +import java.util.Comparator; import java.util.Iterator; import java.util.Map; @@ -27,7 +30,7 @@ private TreeMap(final Set>> tree) { } private static Ord> ord(final Ord keyOrd) { - return keyOrd.comap(P2.__1()); + return keyOrd.contramap(P2.__1()); } /** @@ -37,7 +40,71 @@ 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())); + } + + @Override + public int hashCode() { + return Hash.treeMapHash(Hash.anyHash(), Hash.anyHash()).hash(this); + } + + @Override + public String toString() { + return Show.treeMapShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + /** + * Constructs a tree map from the given elements. + * + * @param keyOrd An order for the keys of the tree map. + * @param p2s The elements to construct the tree map with. + * @return a TreeMap with the given elements. + */ + @SafeVarargs public static TreeMap treeMap(final Ord keyOrd, final P2... p2s) { + return arrayTreeMap(keyOrd, p2s); + } + + /** + * Constructs a tree map from the given elements. + * + * @param keyOrd An order for the keys of the tree map. + * @param it The elements to construct the tree map with. + * @return A TreeMap with the given elements. + */ + public static TreeMap iterableTreeMap(final Ord keyOrd, final Iterable> it) { + TreeMap tm = empty(keyOrd); + for (final P2 p2 : it) { + tm = tm.set(p2._1(), p2._2()); + } + return tm; + } + + /** + * Constructs a tree map from the given elements. + * + * @param keyOrd An order for the keys of the tree map. + * @param it The elements to construct the tree map with. + * @return A TreeMap with the given elements. + */ + public static TreeMap iteratorTreeMap(final Ord keyOrd, final Iterator> it) { + return iterableTreeMap(keyOrd, () -> it); + } + + /** + * Constructs a tree map from the given elements. + * + * @param keyOrd An order for the keys of the tree map. + * @param ps The elements to construct the tree map with. + * @return A TreeMap with the given elements. + */ + @SafeVarargs + public static TreeMap arrayTreeMap(final Ord keyOrd, final P2...ps) { + return iterableTreeMap(keyOrd, Array.array(ps)); } /** @@ -47,8 +114,7 @@ public static TreeMap empty(final Ord keyOrd) { * @return A potential value for the given key. */ public Option 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); } /** @@ -60,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)))); } /** @@ -70,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()))); } /** @@ -97,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())))); } /** @@ -106,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()); } /** @@ -116,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())); } /** @@ -126,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(); } /** @@ -136,13 +202,31 @@ public Iterator> iterator() { * @return A new mutable map isomorphic to this tree map. */ public Map toMutableMap() { - final Map m = new java.util.TreeMap(); + final F>> fakePair = k -> p(k, Option.none()); + final Comparator comparator = tree.ord().contramap(fakePair).toComparator(); + final Map m = new java.util.TreeMap<>(comparator); for (final P2 e : this) { m.put(e._1(), e._2()); } return m; } + public Stream> toStream() { + return tree.toStream().map(p -> p.map2(o -> o.some())); + } + + public Stream> toStreamReverse() { + return tree.toStreamReverse().map(p -> p.map2(o -> o.some())); + } + + public List> toList() { + return tree.toList().map(p -> p.map2(o -> o.some())); + } + + public List> toListReverse() { + return tree.toListReverse().map(p -> p.map2(o -> o.some())); + } + /** * An immutable projection of the given mutable map. * @@ -164,11 +248,7 @@ public static TreeMap fromMutableMap(final Ord ord, final Map> get() { - return new F>() { - public Option f(final K k) { - return get(k); - } - }; + return this::get; } /** @@ -181,8 +261,8 @@ public Option f(final K k) { */ public P2> update(final K k, final F f) { final P2>>> up = - tree.update(p(k, Option.none()), 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())); } /** @@ -212,11 +292,48 @@ public TreeMap update(final K k, final F f, final V v) { * key in this map, all the elements in the second set are mapped to keys greater than the given key, * and the optional value is the value associated with the given key if present, otherwise None. */ - public P3, Option, Set> split(final K k) { - final F>>, Set> getSome = F1Functions.mapSet(F1Functions.o(Option.fromSome(), P2.>__2()) - , tree.ord().comap(F1Functions.o(P.>p2().f(k), Option.some_()))); - return tree.split(p(k, Option.none())).map1(getSome).map3(getSome) - .map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.>__2()))); + public P3, Option, Set> split(Ord ord, final K k) { + 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())); + } + + /** + * 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. + */ + 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()) { + return tm.set(p2._1(), opt.some()); + } + return tm; + }, empty); + return tree; + } + + /** + * Splits this TreeMap at the given key. Returns a triple of: + *
    + *
  • A tree map containing all the values of this map associated with keys less than the given key.
  • + *
  • An option of a value mapped to the given key, if it exists in this map, otherwise None. + *
  • A tree map containing all the values of this map associated with keys greater than the given key.
  • + *
+ * + * @param k A key at which to split this map. + * @return Two tree maps and an optional value, where all keys in the first tree map are mapped + * to keys less than the given key in this map, all the keys in the second tree map are mapped + * to keys greater than the given key, and the optional value is the value associated with the + * given key if present, otherwise None. + */ + public P3, Option, TreeMap> splitLookup(final K k) { + P3>>, Option>>, Set>>> p3 = tree.split(p(k, get(k))); + Ord o = tree.ord().contramap(k2 -> p(k2, Option.none())); + return p(setTreeMap(o, p3._1()), get(k), setTreeMap(o, p3._3())); } /** @@ -225,12 +342,73 @@ public P3, Option, Set> split(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 Ord o = tree.ord().comap(coord); - return new TreeMap(tree.map(TreeMap.>ord(o), g)); + 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)); } + /** + * Returns the minimum (key, value) pair in the tree if the tree is not empty. + */ + public Option> min() { + return tree.min().map(p -> p(p._1(), p._2().some())); + } + + /** + * Returns the minimum key in the tree if the tree is not empty. + */ + public Option minKey() { + return tree.min().map(P2::_1); + } + + /** + * Returns the maximum (key, value) pair in the tree if the tree is not empty. + */ + public Option> max() { + return tree.max().map(p -> p(p._1(), p._2().some())); + } + + /** + * Returns the maximum key in the tree if the tree is not empty. + */ + public Option maxKey() { + return tree.max().map(P2::_1); + } + + /** + * The expression t1.union(t2) takes the left-biased union of t1 + * and t2. It prefers t1 when duplicate keys are encountered. + * + * @param t2 The other tree we wish to combine with this one + * @return The combined TreeMap + */ + public TreeMap union(TreeMap t2) { + // TODO This could be implemented more efficiently using "hedge union" + TreeMap result = t2; + for(P2 p : this) { + result = result.set(p._1(), p._2()); + } + return result; + } + + /** + * The expression t1.union(t2) takes the left-biased union of t1 + * and t2. It prefers t1 when duplicate keys are encountered. + * + * @param t2 The other list/set of pairs we wish to combine with this one + * @return The combined TreeMap + */ + public TreeMap union(Iterable> t2) { + TreeMap result = this; + for(P2 p : t2) { + if(!this.contains(p._1())) { + result = result.set(p._1(), p._2()); + } + } + return result; + } + } diff --git a/core/src/main/java/fj/data/TreeZipper.java b/core/src/main/java/fj/data/TreeZipper.java index 860d7e80..545238f3 100644 --- a/core/src/main/java/fj/data/TreeZipper.java +++ b/core/src/main/java/fj/data/TreeZipper.java @@ -1,739 +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( - new F4, Stream>, Stream>, Stream>, A, Stream>>>, TreeZipper>() { - public TreeZipper f(final Tree tree, - final Stream> lefts, - final Stream> rights, - final Stream>, A, Stream>>> parents) { - return 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 new F< - TreeZipper, - P4, - Stream>, - Stream>, - Stream>, A, Stream>>>>>() { - public P4< - Tree, - Stream>, - Stream>, - Stream>, A, Stream>>>> f(final TreeZipper a) { - return 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))))).comap(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))))).comap(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 new F, TreeZipper>() { - public TreeZipper f(final TreeZipper a) { - return 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(new F, Tree>() { - public Tree f(final Tree t) { - return 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(new F, Tree>() { - public Tree f(final Tree t) { - return 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( - new F>, A, Stream>>, P3>, B, Stream>>>() { - public P3>, B, Stream>> f(final P3>, A, Stream>> p) { - return 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 new F, TreeZipper>() { - public TreeZipper f(final Tree t) { - return 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 new F, Option>>() { - public Option> f(final TreeZipper z) { - return 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 new F, Option>>() { - public Option> f(final TreeZipper z) { - return 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( - new F>, Option>, Option>>>>() { - public Option>, Option>>> f(final Option> 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 new F, P2, P1>>>>() { - public P2, P1>>> f(final TreeZipper tz) { - return P., P1>>>p(tz, new P1>>() { - private F>, Option, Option>>>> fwd() { - return new F>, Option, Option>>>>() { - public Option, Option>>> f(final Option> 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 new F2, Boolean>, TreeZipper, Option>>() { - public Option> f(final F, Boolean> f, final TreeZipper az) { - return 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 f71afb13..5c1985a4 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -1,22 +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 java.util.Iterator; +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; /** * 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; @@ -30,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(); } @@ -39,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(); } @@ -48,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 @@ -60,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 @@ -74,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); } @@ -83,8 +83,8 @@ public X validation(final F fail, final F success) { * * @return a failing projection of this validation. */ - public FailProjection f() { - return new FailProjection(this); + public final FailProjection f() { + return new FailProjection<>(this); } /** @@ -92,7 +92,7 @@ public FailProjection f() { * * @return An either projection of this validation. */ - public Either toEither() { + public final Either toEither() { return e; } @@ -102,7 +102,7 @@ public Either toEither() { * @param err The error message to fail with. * @return The success value. */ - public T successE(final P1 err) { + public final T successE(final F0 err) { return e.right().valueE(err); } @@ -112,7 +112,7 @@ public T successE(final P1 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)); } @@ -122,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 P1 t) { + public final T orSuccess(final F0 t) { return e.right().orValue(t); } @@ -132,7 +132,7 @@ public T orSuccess(final P1 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)); } @@ -142,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); } @@ -152,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); } @@ -161,8 +161,8 @@ public Unit foreach(final F f) { * * @param f The side-effect to execute. */ - public void foreach(final Effect1 f) { - e.right().foreach(f); + public final void foreachDoEffect(final Effect1 f) { + e.right().foreachDoEffect(f); } /** @@ -171,11 +171,11 @@ public void foreach(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())); } /** @@ -184,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()); } /** @@ -195,8 +195,20 @@ 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)); + } + + /** + * If list contains a failure, returns a failure of the reduction of + * all the failures using the semigroup, otherwise returns the successful list. + */ + public static Validation> sequence(final Semigroup s, final List> list) { + if (list.exists(Validation::isFail)) { + return Validation.fail(list.filter(Validation::isFail).map(v -> v.fail()).foldLeft1((F2) s::sum)); + } else { + return success(list.foldLeft((List acc, Validation v) -> acc.cons(v.success()), List.nil()).reverse()); + } } /** @@ -207,8 +219,8 @@ public Validation sequence(final Validation v) { * @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()); } /** @@ -217,12 +229,8 @@ 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) { - return v.bind(new F, Validation>() { - public Validation f(final F f) { - return map(f); - } - }); + public final Validation apply(final Validation> v) { + return v.bind(this::map); } /** @@ -233,7 +241,7 @@ public Validation f(final F f) { * @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); } @@ -245,16 +253,26 @@ 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 final boolean equals(Object other) { + return Equal.equals0(Validation.class, this, other, () -> Equal.validationEqual(Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.validationHash(Hash.anyHash(), Hash.anyHash()).hash(this); + } + /** * Returns a single element list if this is a success value, otherwise an empty list. * * @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(); } @@ -263,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(); } @@ -272,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(); } @@ -281,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(); } @@ -294,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())); } /** @@ -315,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)); } @@ -329,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))); } @@ -341,12 +359,8 @@ 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) { - return accumulate(s, va, new F2() { - public Unit f(final T t, final A a) { - return unit(); - } - }).f().toOption(); + public final Option accumulate(final Semigroup s, final Validation va) { + return accumulate(s, va, (t, a) -> unit()).f().toOption(); } /** @@ -360,8 +374,8 @@ public Unit f(final T t, final A a) { * @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)); } @@ -376,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))); } @@ -390,12 +404,8 @@ 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) { - return accumulate(s, va, vb, new F3() { - public Unit f(final T t, final A a, final B b) { - return unit(); - } - }).f().toOption(); + public final Option accumulate(final Semigroup s, final Validation va, final Validation vb) { + return accumulate(s, va, vb, (t, a, b) -> unit()).f().toOption(); } /** @@ -410,9 +420,9 @@ public Unit f(final T t, final A a, final B b) { * @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)); } @@ -428,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))); } @@ -444,13 +454,9 @@ 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) { - return accumulate(s, va, vb, vc, new F4() { - public Unit f(final T t, final A a, final B b, final C c) { - return unit(); - } - }).f().toOption(); + 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(); } /** @@ -466,10 +472,10 @@ public Unit f(final T t, final A a, final B b, final C c) { * @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 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)); } @@ -486,9 +492,9 @@ public Unit f(final T t, final A a, final B b, final C c) { * @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 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))); } @@ -503,13 +509,9 @@ public Unit f(final T t, final A a, final B b, final C c) { * @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) { - return accumulate(s, va, vb, vc, vd, new F5() { - public Unit f(final T t, final A a, final B b, final C c, final D d) { - return unit(); - } - }).f().toOption(); + 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(); } /** @@ -526,10 +528,10 @@ public Unit f(final T t, final A a, final B b, final C c, final D d) { * @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)); } @@ -547,10 +549,10 @@ public Unit f(final T t, final A a, final B b, final C c, final D d) { * @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))); } @@ -566,14 +568,10 @@ public Unit f(final T t, final A a, final B b, final C c, final D d) { * @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) { - return accumulate(s, va, vb, vc, vd, ve, new F6() { - public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e) { - return unit(); - } - }).f().toOption(); + 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(); } /** @@ -591,11 +589,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e) * @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)); } @@ -614,11 +612,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e) * @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))); } @@ -635,15 +633,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e) * @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) { - return accumulate(s, va, vb, vc, vd, ve, vf, new F7() { - public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, final F$ f) { - return unit(); - } - }).f().toOption(); + 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(); } /** @@ -662,11 +656,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, * @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)); } @@ -686,11 +680,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, * @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))); } @@ -708,15 +702,11 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, * @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) { - return accumulate(s, va, vb, vc, vd, ve, vf, vg, new F8() { - public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, final F$ f, final G g) { - return unit(); - } - }).f().toOption(); + 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(); } /** @@ -724,129 +714,437 @@ public Unit f(final T t, final A a, final B b, final C c, final D d, final E$ e, * * @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) { - List list = fails(List.list(this, v2, v3)); + 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) { - List list = fails(List.list(this, v2, v3, v4)); + 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) { - List list = fails(List.list(this, v2, v3, v4, v5)); + 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.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.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.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())); } } + /** + * If the list contains a failure, returns a Validation of the list of + * fails in the list, otherwise returns a successful Validation with + * the list of successful values. Does not accumulate the failures into a + * single failure using a semigroup. + */ + public static Validation, List> sequenceNonCumulative(List> list) { + if (list.exists(Validation::isFail)) { + F2, Validation, List> f = (acc, v) -> acc.cons(v.fail()); + return fail(list.filter(Validation::isFail).foldLeft(f, List.nil()).reverse()); + } else { + F2, Validation, List> f = (acc, v) -> acc.cons(v.success()); + return success(list.filter(Validation::isSuccess).foldLeft(f, List.nil()).reverse()); + } + } + /** + * 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 static Validation, List> sequence(List> list) { - F2, Validation, List>, Validation, List>> f2 = (v, acc) -> { - if (acc.isFail() && v.isFail()) { - return Validation.validation(acc.toEither().left().map(l -> l.cons(v.fail()))); - } else if (acc.isSuccess() && v.isSuccess()) { - return acc.map(l -> l.cons(v.success())); - } else { - return acc; - } - }; - return list.foldRight(f2, Validation.success(List.nil())); - } + /** + * 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()); + } + /** + * 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()); + } + /** + * 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 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 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()); + } - 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 an option. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the option value + * @return the option + */ + public static final Option> sequenceOption(final Validation> validation) { + return validation.traverseOption(identity()); + } + + /** + * Sequence the given validation and collect the output as a P1. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the P1 value + * @return the P1 + */ + public static final P1> sequenceP1(final Validation> validation) { + return validation.traverseP1(identity()); + } + + + /** + * 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. */ - public final class FailProjection implements Iterable { + public static final class FailProjection implements Iterable { private final Validation v; private FailProjection(final Validation v) { @@ -868,7 +1166,7 @@ public Validation validation() { * @param err The error message to fail with. * @return The failing value. */ - public E failE(final P1 err) { + public E failE(final F0 err) { return v.toEither().left().valueE(err); } @@ -888,7 +1186,7 @@ public E failE(final String err) { * @param e The value to return if this is success. * @return The failing value or the given value. */ - public E orFail(final P1 e) { + public E orFail(final F0 e) { return v.toEither().left().orValue(e); } @@ -927,8 +1225,8 @@ public Unit foreach(final F f) { * * @param f The side-effect to execute. */ - public void foreach(final Effect1 f) { - v.toEither().left().foreach(f); + public void foreachDoEffect(final Effect1 f) { + v.toEither().left().foreachDoEffect(f); } /** @@ -948,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()); } /** @@ -958,18 +1256,10 @@ public Validation bind(final F> f) { * @return A new validation value after the final join. */ public Validation sequence(final Validation v) { - return bind(new F>() { - public Validation f(final E e) { - return v; - } - }); + return bind(e1 -> v); } - - - - /** * Returns None if this is a success or if the given predicate p does not hold for the * failing value, otherwise, returns a fail in Some. @@ -979,7 +1269,7 @@ public Validation f(final E e) { * 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()); } /** @@ -989,11 +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(new F, Validation>() { - public Validation f(final F f) { - return map(f); - } - }); + return v.f().bind(this::map); } /** @@ -1071,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())); } /** @@ -1085,7 +1371,7 @@ public Validation, T> nel() { * @return A validation using the given either value. */ public static Validation validation(final Either e) { - return new Validation(e); + return new Validation<>(e); } /** @@ -1094,11 +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 new F, Validation>() { - public Validation f(final Either e) { - return validation(e); - } - }; + return Validation::validation; } /** @@ -1107,11 +1389,7 @@ public Validation f(final Either e) { * @return A function that constructs an either with a validation. */ public static F, Either> either() { - return new F, Either>() { - public Either f(final Validation v) { - return v.toEither(); - } - }; + return Validation::toEither; } /** @@ -1121,7 +1399,7 @@ public Either f(final Validation v) { * @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)); } /** @@ -1131,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)); } /** @@ -1154,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); } /** @@ -1174,11 +1452,7 @@ public static Validation parseByte(final String s) /** * A function that parses a string into a byte. */ - public static final F> parseByte = new F>() { - public Validation f(final String s) { - return parseByte(s); - } - }; + public static final F> parseByte = Validation::parseByte; /** * Parses the given string into a double. @@ -1197,11 +1471,7 @@ public static Validation parseDouble(final String /** * A function that parses a string into a double. */ - public static final F> parseDouble = new F>() { - public Validation f(final String s) { - return parseDouble(s); - } - }; + public static final F> parseDouble = Validation::parseDouble; /** * Parses the given string into a float. @@ -1220,11 +1490,7 @@ public static Validation parseFloat(final String s /** * A function that parses a string into a float. */ - public static final F> parseFloat = new F>() { - public Validation f(final String s) { - return parseFloat(s); - } - }; + public static final F> parseFloat = Validation::parseFloat; /** * Parses the given string into a integer. @@ -1243,11 +1509,7 @@ public static Validation parseInt(final String s /** * A function that parses a string into an integer. */ - public static final F> parseInt = new F>() { - public Validation f(final String s) { - return parseInt(s); - } - }; + public static final F> parseInt = Validation::parseInt; /** * Parses the given string into a long. @@ -1266,11 +1528,7 @@ public static Validation parseLong(final String s) /** * A function that parses a string into a long. */ - public static final F> parseLong = new F>() { - public Validation f(final String s) { - return parseLong(s); - } - }; + public static final F> parseLong = Validation::parseLong; /** * Parses the given string into a short. @@ -1287,15 +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 = new F>() { - public Validation f(final String s) { - return 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( + list.filter(Validation::isFail).map(v -> v.fail()), + list.filter(Validation::isSuccess).map(v -> v.success()) + ); + } - public String toString() { + @Override + 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 3aea8826..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; @@ -29,12 +26,16 @@ public W log() { return logValue; } + public Monoid monoid() { + return 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) { @@ -55,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 c94a8129..4b96377f 100644 --- a/core/src/main/java/fj/data/Zipper.java +++ b/core/src/main/java/fj/data/Zipper.java @@ -1,619 +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; -import static fj.F2Functions.*; - -/** - * 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 new F3, A, Stream, Zipper>() { - public Zipper f(final Stream l, final A a, final Stream r) { - return 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 new F, P3, A, Stream>>() { - public P3, A, Stream> f(final Zipper a) { - return 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).comap(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).comap(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).comap(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 new F, Option>>() { - public Option> f(final Zipper as) { - return 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 new F, Option>>() { - public Option> f(final Zipper as) { - return 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( - new F, Option, Zipper>>>() { - public Option, Zipper>> f(final Zipper p) { - return p.previous().map(join(P., Zipper>p2())); - } - }, this); - final Stream> right = Stream.unfold( - new F, Option, Zipper>>>() { - public Option, Zipper>> f(final Zipper p) { - return 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(new F2, Option>>() { - public Option> f(final Integer i, final Zipper a) { - return 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(new F, Boolean>() { - public Boolean f(final Zipper zipper) { - return 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 f53a7e28..f9d6eb83 100644 --- a/core/src/main/java/fj/data/fingertrees/Deep.java +++ b/core/src/main/java/fj/data/fingertrees/Deep.java @@ -1,13 +1,16 @@ package fj.data.fingertrees; -import fj.F; -import fj.Function; -import fj.P2; +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; + +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. @@ -56,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)); } /** @@ -97,216 +100,193 @@ public V measure() { final Measured m = measured(); final V measure = m.sum(m.measure(a), v); final MakeTree mk = mkTree(m); - return prefix.match(new F, FingerTree>() { - public FingerTree f(final One one) { - return new Deep(m, measure, mk.two(a, one.value()), middle, suffix); - } - }, new F, FingerTree>() { - public FingerTree f(final Two two) { - return new Deep(m, measure, mk.three(a, two.values()._1(), two.values()._2()), middle, suffix); - } - }, new F, FingerTree>() { - public FingerTree f(final Three three) { - return new Deep(m, measure, mk.four(a, three.values()._1(), three.values()._2(), - three.values()._3()), middle, suffix); - } - }, new F, FingerTree>() { - public FingerTree f(final Four four) { - return new Deep(m, measure, mk.two(a, four.values()._1()), - middle.cons(mk.node3(four.values()._2(), four.values()._3(), four.values()._4())), - suffix); - } - }); + + return prefix.match( + one -> new Deep<>(m, measure, mk.two(a, one.value()), middle, suffix), + two -> new Deep<>(m, measure, mk.three(a, two.values()._1(), two.values()._2()), middle, suffix), + three -> new Deep<>(m, measure, mk.four(a, three.values()._1(), three.values()._2(), three.values()._3()), middle, suffix), + four -> new Deep<>(m, measure, mk.two(a, four.values()._1()), middle.cons(mk.node3(four.values()._2(), four.values()._3(), four.values()._4())), suffix)); } public FingerTree snoc(final A a) { final Measured m = measured(); final V measure = m.sum(m.measure(a), v); final MakeTree mk = mkTree(m); - return suffix.match(new F, FingerTree>() { - public FingerTree f(final One one) { - return new Deep(m, measure, prefix, middle, mk.two(one.value(), a)); - } - }, new F, FingerTree>() { - public FingerTree f(final Two two) { - return new Deep(m, measure, prefix, middle, mk.three(two.values()._1(), two.values()._2(), a)); - } - }, new F, FingerTree>() { - public FingerTree f(final Three three) { - return new Deep(m, measure, prefix, middle, mk.four(three.values()._1(), three.values()._2(), - three.values()._3(), a)); - } - }, new F, FingerTree>() { - public FingerTree f(final Four four) { - return new Deep(m, measure, prefix, - middle.snoc(mk.node3(four.values()._1(), four.values()._2(), four.values()._3())), - mk.two(four.values()._4(), a)); - } - }); + + return suffix.match( + one -> new Deep<>(m, measure, prefix, middle, mk.two(one.value(), a)), + two -> new Deep<>(m, measure, prefix, middle, mk.three(two.values()._1(), two.values()._2(), a)), + three -> new Deep<>(m, measure, prefix, middle, mk.four(three.values()._1(), three.values()._2(), three.values()._3(), a)), + four -> new Deep<>(m, measure, prefix, middle.snoc(mk.node3(four.values()._1(), four.values()._2(), four.values()._3())), mk.two(four.values()._4(), a))); + } + + @Override public A head() { + return prefix.match( + One::value, + two -> two.values()._1(), + three -> three.values()._1(), + four -> four.values()._1()); } + @Override public A last() { + return suffix.match( + One::value, + two -> two.values()._2(), + three -> three.values()._3(), + four -> four.values()._4()); + } + + 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 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) + ); + } + + @Override public FingerTree tail() { return deepL(measured(), prefix.tail(), middle, suffix); } + + @Override public FingerTree init() { return deepR(measured(), suffix.init(), middle, prefix); } + @Override public FingerTree append(final FingerTree t) { final Measured m = measured(); - return t.match(Function., FingerTree>constant(t), new F, FingerTree>() { - public FingerTree f(final Single single) { - return t.snoc(single.value()); - } - }, new F, FingerTree>() { - public FingerTree f(final Deep deep) { - return new Deep(m, m.sum(measure(), deep.measure()), prefix, - addDigits0(m, middle, suffix, deep.prefix, deep.middle), deep.suffix); + return t.match( + constant(this), + single -> snoc(single.value()), + deep -> new Deep<>(m, m.sum(measure(), deep.measure()), prefix, + addDigits0(m, middle, suffix, deep.prefix, deep.middle), deep.suffix)); + } + + @Override P3, A, FingerTree> split1(final F predicate, final V acc) { + final Measured m = measured(); + final V accL = m.sum(acc, prefix.measure()); + if (predicate.f(accL)) { + final P3>, A, Option>> lxr = prefix.split1(predicate, acc); + return P.p(lxr._1().option(new Empty<>(m), Digit::toTree), lxr._2(), deepL(m, lxr._3(), middle, suffix)); + } else { + final V accM = m.sum(accL, middle.measure()); + if (predicate.f(accM)) { + final P3>, Node, FingerTree>> mlXsMr = middle.split1(predicate, accL); + final P3>, A, Option>> lxr = mlXsMr._2().split1(predicate, m.sum(accL, mlXsMr._1().measure())); + return P.p(deepR(m, lxr._1(), mlXsMr._1(), prefix), lxr._2(), deepL(m, lxr._3(), mlXsMr._3(), suffix)); + } else { + final P3>, A, Option>> lxr = suffix.split1(predicate, accM); + return P.p(deepR(m, lxr._1(), middle, prefix), lxr._2(), lxr._3().option(new Empty<>(m), Digit::toTree)); } - }); + } } - @SuppressWarnings({"ReturnOfNull", "IfStatementWithIdenticalBranches"}) @Override public P2 lookup(final F o, final int i) { final int spr = o.f(prefix.measure()); - final int spm = o.f(middle.measure()); - if (i < spr) - return null; // TODO - //return prefix.lookup(o, i); - if (i < spm) { - return null; // TODO - /* final P2> p = middle.lookup(o, i - spr); - return p._2().lookup(o, p._1()); */ + if (i < spr) { + return prefix.lookup(o, i); + } else { + final int spm = spr + o.f(middle.measure()); + if (i < spm) { + final P2> p = middle.lookup(o, i - spr); + return p._2().lookup(o, p._1()); + } else { + return suffix.lookup(o, i - spm); + } } - return null; // TODO suffix.lookup(i - spm); } - private static FingerTree> addDigits0(final Measured m, final FingerTree> m1, - final Digit s1, final Digit p2, - final FingerTree> m2) { - final MakeTree mk = mkTree(m); - return s1.match(new F, FingerTree>>() { - public FingerTree> f(final One one1) { - return p2.match(new F, FingerTree>>() { - public FingerTree> f(final One one2) { - return append1(m, m1, mk.node2(one1.value(), one2.value()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Two two2) { - final V2 vs = two2.values(); - return append1(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Three three) { - final V3 vs = three.values(); - return append2(m, m1, mk.node2(one1.value(), vs._1()), mk.node2(vs._2(), vs._3()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Four 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); - } - }); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Two two1) { - final V2 v1 = two1.values(); - return p2.match(new F, FingerTree>>() { - public FingerTree> f(final One one) { - return append1(m, m1, mk.node3(v1._1(), v1._2(), one.value()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Two two2) { - final V2 v2 = two2.values(); - return append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v2._1(), v2._2()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Three three) { - final V3 v2 = three.values(); - return append2(m, m1, mk.node3(v1._1(), v1._2(), 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(v1._1(), v1._2(), v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); - } - }); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Three three1) { - final V3 v1 = three1.values(); - return p2.match(new F, FingerTree>>() { - public FingerTree> f(final One one) { - return append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v1._3(), 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.node2(v2), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Three three2) { - return append2(m, m1, mk.node3(v1), mk.node3(three2.values()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Four four) { - return append3(m, m1, mk.node3(v1), mk.node2(four.values()._1(), four.values()._2()), - mk.node2(four.values()._3(), four.values()._4()), m2); - } - }); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Four four1) { - final V4 v1 = four1.values(); - return p2.match(new F, FingerTree>>() { - public FingerTree> f(final One one) { - return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), one.value()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Two two) { - final V2 v2 = two.values(); - return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), 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(), v1._3()), mk.node2(v1._4(), v2._1()), - mk.node2(v2._2(), v2._3()), m2); - } - }, new F, FingerTree>>() { - public FingerTree> f(final Four four2) { - final V4 v2 = four2.values(); - return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), v2._1(), v2._2()), - mk.node2(v2._3(), v2._4()), m2); - } - }); - } - }); - } + @Override + public int length() { + int midSize = middle.foldLeft((acc, n) -> acc + n.length(), 0); + return prefix.length() + midSize + suffix.length(); + } + + private static FingerTree> addDigits0( + final Measured m, final FingerTree> m1, + final Digit s1, final Digit p2, + final FingerTree> m2) { + + final MakeTree mk = mkTree(m); + return s1.match( + 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( + one -> append1(m, m1, mk.node3(v1._1(), v1._2(), one.value()), m2), + two2 -> { + final V2 v2 = two2.values(); + return append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v2._1(), v2._2()), m2); + }, + three -> { + final V3 v2 = three.values(); + return append2(m, m1, mk.node3(v1._1(), v1._2(), v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, + four -> { + final V4 v2 = four.values(); + return append2(m, m1, mk.node3(v1._1(), v1._2(), v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2); + } + ); + }, + three1 -> { + final V3 v1 = three1.values(); + return p2.match( + one -> append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v1._3(), one.value()), m2), + two -> { + final V2 v2 = two.values(); + return append2(m, m1, mk.node3(v1), mk.node2(v2), m2); + }, + three2 -> append2(m, m1, mk.node3(v1), mk.node3(three2.values()), m2), + four -> append3(m, m1, mk.node3(v1), + mk.node2(four.values()._1(), four.values()._2()), + mk.node2(four.values()._3(), four.values()._4()), m2 + ) + ); + }, + four1 -> { + final V4 v1 = four1.values(); + return p2.match( + one -> append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), one.value()), m2), + two -> { + final V2 v2 = two.values(); + return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), v2._1(), v2._2()), m2); + }, + three -> { + final V3 v2 = three.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), v2._1()), mk.node2(v2._2(), v2._3()), m2); + }, + four2 -> { + final V4 v2 = four2.values(); + return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2); + } + ); + } + ); + } 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, @@ -315,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, @@ -454,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); + }); }); } @@ -559,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, @@ -597,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); + }); }); } @@ -708,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, @@ -746,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 09e92d62..fe76d1df 100644 --- a/core/src/main/java/fj/data/fingertrees/Digit.java +++ b/core/src/main/java/fj/data/fingertrees/Digit.java @@ -1,11 +1,12 @@ package fj.data.fingertrees; -import fj.F; -import fj.F2; -import fj.Function; +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; + import static fj.data.fingertrees.FingerTree.mkTree; /** @@ -36,28 +37,23 @@ public abstract class Digit { * @param f A function with which to fold this digit. * @return The right reduction of this digit with the given function. */ - public final A reduceRight(final F> f) { - return match(new F, A>() { - public A f(final One one) { - return one.value(); - } - }, new F, A>() { - public A f(final Two two) { - final V2 v = two.values(); - return f.f(v._1()).f(v._2()); - } - }, new F, A>() { - public A f(final Three three) { - final V3 v = three.values(); - return f.f(v._1()).f(f.f(v._2()).f(v._3())); - } - }, new F, A>() { - public A f(final Four four) { - final V4 v = four.values(); - return f.f(v._1()).f(f.f(v._2()).f(f.f(v._3()).f(v._4()))); - } - }); - } + public final A reduceRight(final F> f) { + return match( + One::value, + two -> { + final V2 v = two.values(); + return f.f(v._1()).f(v._2()); + }, + three -> { + final V3 v = three.values(); + return f.f(v._1()).f(f.f(v._2()).f(v._3())); + }, + four -> { + final V4 v = four.values(); + return f.f(v._1()).f(f.f(v._2()).f(f.f(v._3()).f(v._4()))); + } + ); + } /** * Folds this digit to the right using the given function. @@ -66,26 +62,21 @@ public A f(final Four four) { * @return The right reduction of this digit with the given function. */ public final A reduceLeft(final F> f) { - return match(new F, A>() { - public A f(final One one) { - return one.value(); - } - }, new F, A>() { - public A f(final Two two) { - final V2 v = two.values(); - return f.f(v._1()).f(v._2()); - } - }, new F, A>() { - public A f(final Three three) { - final V3 v = three.values(); - return f.f(f.f(v._1()).f(v._2())).f(v._3()); - } - }, new F, A>() { - public A f(final Four four) { - final V4 v = four.values(); - return f.f(f.f(f.f(v._1()).f(v._2())).f(v._3())).f(v._4()); - } - }); + return match( + One::value, + two -> { + final V2 v = two.values(); + return f.f(v._1()).f(v._2()); + }, + three -> { + final V3 v = three.values(); + return f.f(f.f(v._1()).f(v._2())).f(v._3()); + }, + four -> { + final V4 v = four.values(); + return f.f(f.f(f.f(v._1()).f(v._2())).f(v._3())).f(v._4()); + } + ); } /** @@ -97,23 +88,12 @@ public A f(final Four four) { * with the given function and measured with the given measuring. */ public final Digit map(final F f, final Measured m) { - return match(new F, Digit>() { - public Digit f(final One one) { - return new One(m, f.f(one.value())); - } - }, new F, Digit>() { - public Digit f(final Two two) { - return new Two(m, two.values().map(f)); - } - }, new F, Digit>() { - public Digit f(final Three three) { - return new Three(m, three.values().map(f)); - } - }, new F, Digit>() { - public Digit f(final Four four) { - return new Four(m, four.values().map(f)); - } - }); + 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)) + ); } /** @@ -134,43 +114,60 @@ public abstract B match(final F, B> one, final F, B> two this.m = m; } + final Measured measured() { return m; } + /** * Returns the sum of the measurements of this digit according to the monoid. * * @return the sum of the measurements of this digit according to the monoid. */ public final V measure() { - return foldLeft(Function.curry(new F2() { - public V f(final V v, final A a) { - return m.sum(v, m.measure(a)); - } - }), m.zero()); + return foldLeft(Function.curry((v, a) -> m.sum(v, m.measure(a))), m.zero()); } /** * Returns the tree representation of this digit. * @return the tree representation of this digit. */ - public final FingerTree toTree() { - final MakeTree mk = mkTree(m); - return match(new F, FingerTree>() { - public FingerTree f(final One one) { - return mk.single(one.value()); - } - }, new F, FingerTree>() { - public FingerTree f(final Two two) { - return mk.deep(mk.one(two.values()._1()), new Empty>(m.nodeMeasured()), mk.one(two.values()._2())); - } - }, new F, FingerTree>() { - public FingerTree f(final Three three) { - return mk.deep(mk.two(three.values()._1(), three.values()._2()), new Empty>(m.nodeMeasured()), - mk.one(three.values()._3())); - } - }, new F, FingerTree>() { - public FingerTree f(final Four four) { - return mk.deep(mk.two(four.values()._1(), four.values()._2()), new Empty>(m.nodeMeasured()), - mk.two(four.values()._3(), four.values()._4())); - } - }); + 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())) + ); + } + + 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())) + ); } + + 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())) + ); + } + + abstract P3>, A, Option>> split1(final F predicate, final V acc); + + public abstract P2 lookup(final F o, final int i); + + 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 cb5fe863..275b1cec 100644 --- a/core/src/main/java/fj/data/fingertrees/Empty.java +++ b/core/src/main/java/fj/data/fingertrees/Empty.java @@ -2,6 +2,10 @@ import fj.F; import fj.P2; +import fj.P3; +import fj.Show; +import fj.data.Stream; + import static fj.Bottom.error; /** @@ -13,22 +17,33 @@ 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) { return cons(a); } + @Override public A head() { throw error("Selection of head in empty tree"); } + + @Override public A last() { throw error("Selection of last in empty tree"); } + + @Override public FingerTree tail() { throw error("Selection of tail in empty tree"); } + + @Override public FingerTree init() { throw error("Selection of init in empty tree"); } + @Override public FingerTree append(final FingerTree t) { return t; } - @Override public P2 lookup(final F o, final int i) { - throw error("Lookup of empty tree."); - } + @Override public P2 lookup(final F o, final int i) { throw error("Lookup of empty tree."); } - @Override public B foldRight(final F> aff, final B z) { + @Override + public int length() { + return 0; + } + + @Override public B foldRight(final F> aff, final B z) { return z; } @@ -45,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); } /** @@ -65,5 +80,16 @@ public V measure() { return empty.f(this); } + @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 8fb2f158..ab0effb0 100644 --- a/core/src/main/java/fj/data/fingertrees/FingerTree.java +++ b/core/src/main/java/fj/data/fingertrees/FingerTree.java @@ -1,162 +1,268 @@ -package fj.data.fingertrees; - -import fj.*; -import fj.data.Seq; - -/** - * 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); - - /** - * 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); - - public abstract P2 lookup(final F o, final int i); -} +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 f3242c00..31a40d72 100644 --- a/core/src/main/java/fj/data/fingertrees/Four.java +++ b/core/src/main/java/fj/data/fingertrees/Four.java @@ -1,8 +1,18 @@ package fj.data.fingertrees; +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; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.fingertrees.FingerTree.mkTree; + /** * A four-element prefix or suffix of a finger tree. */ @@ -36,4 +46,57 @@ public B foldLeft(final F> bff, final B z) { public V4 values() { return as; } + + @Override P3>, A, Option>> split1(final F predicate, final V acc) { + final Measured m = measured(); + final MakeTree mk = mkTree(m); + final F measure = m.measure(); + final V acc1 = m.sum(acc, measure.f(as._1())); + if (predicate.f(acc1)) { + return P.p(none(), as._1(), some(mk.three(as._2(), as._3(), as._4()))); + } else { + final V acc2 = m.sum(acc1, measure.f(as._2())); + if (predicate.f(acc2)) { + return P.p(some(mk.one(as._1())), as._2(), some(mk.two(as._3(), as._4()))); + } else if (predicate.f(m.sum(acc2, measure.f(as._3())))) { + return P.p(some(mk.two(as._1(), as._2())), as._3(), some(mk.one(as._4()))); + } else { + return P.p(some(mk.three(as._1(), as._2(), as._3())), as._4(), none()); + } + } + } + + @Override public P2 lookup(final F o, final int i) { + final F m = measured().measure(); + final int s1 = o.f(m.f(as._1())); + if (i < s1) { + return P.p(i, as._1()); + } else { + final int s2 = s1 + o.f(m.f(as._2())); + if (i < s2) { + return P.p(i - s1, as._2()); + } else { + final int s3 = s2 + o.f(m.f(as._3())); + if (i < s3) { + return P.p(i - s2, as._3()); + } else { + return P.p(i - s3, as._4()); + } + } + } + } + + @Override + 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 84ed7655..b2a3da84 100644 --- a/core/src/main/java/fj/data/fingertrees/MakeTree.java +++ b/core/src/main/java/fj/data/fingertrees/MakeTree.java @@ -10,9 +10,11 @@ */ public final class MakeTree { private final Measured m; + private final Empty empty; MakeTree(final Measured m) { this.m = m; + this.empty = new Empty<>(m); } // Tree constructors @@ -23,7 +25,7 @@ public final class MakeTree { * @return The empty tree. */ public FingerTree empty() { - return new Empty(m); + return empty; } /** @@ -33,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); } /** @@ -61,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 @@ -73,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); } /** @@ -84,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)); } /** @@ -96,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)); } /** @@ -109,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 @@ -122,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)); } /** @@ -134,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)); } /** @@ -144,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); } /** @@ -154,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 8d21b310..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,11 +74,7 @@ public V zero() { * @return A measured instance for nodes. */ public Measured> nodeMeasured() { - return new Measured>(m, new F, V>() { - public V f(final Node node) { - return node.measure(); - } - }); + return new Measured<>(m, Node::measure); } /** @@ -87,11 +83,7 @@ public V f(final Node node) { * @return A measured instance for digits. */ public Measured> digitMeasured() { - return new Measured>(m, new F, V>() { - public V f(final Digit d) { - return 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 4428eb8f..ff98b337 100644 --- a/core/src/main/java/fj/data/fingertrees/Node.java +++ b/core/src/main/java/fj/data/fingertrees/Node.java @@ -1,8 +1,12 @@ 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; /** @@ -17,35 +21,22 @@ public abstract class Node { public abstract B foldLeft(final F> f, final B z); public static F, B>> foldLeft_(final F> bff) { - return curry(new F2, B>() { - public B f(final B b, final Node node) { return node.foldLeft(bff, b); } - }); + return curry((b, node) -> node.foldLeft(bff, b)); } public static F, B>> foldRight_(final F> aff) { - return curry(new F2, B>() { - public B f(final B b, final Node node) { return node.foldRight(aff, b); } - }); + return curry((b, node) -> node.foldRight(aff, b)); } public final Node map(final F f, final Measured m) { - return match(new F, Node>() { - public Node f(final Node2 node2) { - return new Node2(m, node2.toVector().map(f)); - } - }, new F, Node>() { - public Node f(final Node3 node3) { - return new Node3(m, node3.toVector().map(f)); - } - }); + return match( + node2 -> new Node2<>(m, node2.toVector().map(f)), + node3 -> new Node3<>(m, node3.toVector().map(f)) + ); } public static F, Node> liftM(final F f, final Measured m) { - return new F, Node>() { - public Node f(final Node node) { - return node.map(f, m); - } - }; + return node -> node.map(f, m); } public abstract Digit toDigit(); @@ -59,11 +50,18 @@ public final V measure() { return measure; } - Measured measured() { + final Measured measured() { return m; } + abstract P3>, A, Option>> split1(final F predicate, final V acc); + public abstract P2 lookup(final F o, final int i); 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 73e72cd9..56e668e7 100644 --- a/core/src/main/java/fj/data/fingertrees/Node2.java +++ b/core/src/main/java/fj/data/fingertrees/Node2.java @@ -1,9 +1,18 @@ package fj.data.fingertrees; +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; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.fingertrees.FingerTree.mkTree; + /** * A two-element inner tree node. */ @@ -24,19 +33,48 @@ 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) { + final Measured m = measured(); + final MakeTree mk = mkTree(m); + if (predicate.f(m.sum(acc, m.measure().f(as._1())))) { + return P.p(none(), as._1(), some(mk.one(as._2()))); + } else { + return P.p(some(mk.one(as._1())), as._2(), none()); + } } - @SuppressWarnings({"ReturnOfNull"}) @Override public P2 lookup(final F o, final int i) { - return null; // TODO + final F m = measured().measure(); + final int s1 = o.f(m.f(as._1())); + if (i < s1) { + return P.p(i, as._1()); + } else { + return P.p(i - s1, as._2()); + } } public B match(final F, B> n2, final F, B> n3) { return n2.f(this); } - public V2 toVector() { + @Override + public int length() { + return 2; + } + + 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 01a74d31..c803239f 100644 --- a/core/src/main/java/fj/data/fingertrees/Node3.java +++ b/core/src/main/java/fj/data/fingertrees/Node3.java @@ -1,9 +1,18 @@ package fj.data.fingertrees; +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; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.fingertrees.FingerTree.mkTree; + /** * A three-element inner tree node. */ @@ -27,16 +36,53 @@ public B match(final F, B> n2, final F, B> n3) { return n3.f(this); } - public Digit toDigit() { - return new Three(measured(), as); + @Override + public int length() { + return 3; + } + + public Digit toDigit() { + return new Three<>(measured(), as); } - @SuppressWarnings({"ReturnOfNull"}) - public P2 lookup(final F o, final int i) { - return null; //TODO + P3>, A, Option>> split1(final F predicate, final V acc) { + final Measured m = measured(); + final MakeTree mk = mkTree(m); + final V acc1 = m.sum(acc, m.measure().f(as._1())); + if (predicate.f(acc1)) { + return P.p(none(), as._1(), some(mk.two(as._2(), as._3()))); + } else if (predicate.f(m.sum(acc1, m.measure().f(as._2())))) { + return P.p(some(mk.one(as._1())), as._2(), some(mk.one(as._3()))); + } else { + return P.p(some(mk.two(as._1(), as._2())), as._3(), none()); + } + } + + @Override public P2 lookup(final F o, final int i) { + final F m = measured().measure(); + final int s1 = o.f(m.f(as._1())); + if (i < s1) { + return P.p(i, as._1()); + } else { + final int s2 = s1 + o.f(m.f(as._2())); + if (i < s2) { + return P.p(i - s1, as._2()); + } else { + return P.p(i - s2, as._3()); + } + } } 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 6df76834..f3711ace 100644 --- a/core/src/main/java/fj/data/fingertrees/One.java +++ b/core/src/main/java/fj/data/fingertrees/One.java @@ -1,6 +1,14 @@ package fj.data.fingertrees; import fj.F; +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; /** * A single-element prefix or suffix of a finger tree. @@ -35,4 +43,26 @@ public B foldLeft(final F> bff, final B z) { public A value() { return a; } + + @Override P3>, A, Option>> split1(final F predicate, final V acc) { + return P.p(none(), a, none()); + } + + @Override public P2 lookup(final F o, final int i) { + return P.p(i, a); + } + + @Override + 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 9fb2819c..57ea0897 100644 --- a/core/src/main/java/fj/data/fingertrees/Single.java +++ b/core/src/main/java/fj/data/fingertrees/Single.java @@ -1,8 +1,14 @@ 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.P.p; +import static fj.Show.anyShow; /** * A tree with a single element. @@ -34,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)); } /** @@ -56,23 +62,39 @@ 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; } + + @Override public A last() { return a; } + + @Override public FingerTree tail() { return new Empty<>(measured()); } + + @Override public FingerTree init() { return new Empty<>(measured()); } + @Override public FingerTree append(final FingerTree t) { return t.cons(a); } - @Override public P2 lookup(final F o, final int i) { - return p(i, a); + @Override P3, A, FingerTree> split1(final F predicate, final V acc) { + final Empty empty = new Empty<>(measured()); + return p(empty, a, empty); } - /** + @Override public P2 lookup(final F o, final int i) { return p(i, a); } + + @Override + public int length() { + return 1; + } + + /** * Returns the single element of this tree. * * @return the single element of this tree. @@ -80,4 +102,13 @@ public V measure() { 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 16f0296e..2db896b3 100644 --- a/core/src/main/java/fj/data/fingertrees/Three.java +++ b/core/src/main/java/fj/data/fingertrees/Three.java @@ -1,8 +1,18 @@ package fj.data.fingertrees; +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; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.fingertrees.FingerTree.mkTree; + /** * A three-element prefix or suffix of a finger tree. */ @@ -36,4 +46,46 @@ public B foldLeft(final F> bff, final B z) { public V3 values() { return as; } + + @Override P3>, A, Option>> split1(final F predicate, final V acc) { + final Measured m = measured(); + final MakeTree mk = mkTree(m); + final F measure = m.measure(); + final V acc1 = m.sum(acc, measure.f(as._1())); + if (predicate.f(acc1)) { + return P.p(none(), as._1(), some(mk.two(as._2(), as._3()))); + } else if (predicate.f(m.sum(acc1, measure.f(as._2())))) { + return P.p(some(mk.one(as._1())), as._2(), some(mk.one(as._3()))); + } else { + return P.p(some(mk.two(as._1(), as._2())), as._3(), none()); + } + } + + @Override public P2 lookup(F o, int i) { + final F m = measured().measure(); + final int s1 = o.f(m.f(as._1())); + if (i < s1) { + return P.p(i, as._1()); + } else { + final int s2 = s1 + o.f(m.f(as._2())); + if (i < s2) { + return P.p(i - s1, as._2()); + } else { + return P.p(i - s2, as._3()); + } + } + } + + @Override + 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 08108820..cb8a2162 100644 --- a/core/src/main/java/fj/data/fingertrees/Two.java +++ b/core/src/main/java/fj/data/fingertrees/Two.java @@ -1,8 +1,18 @@ package fj.data.fingertrees; +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; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.fingertrees.FingerTree.mkTree; + /** * A two-element prefix or suffix of a finger tree. */ @@ -36,4 +46,38 @@ public B foldLeft(final F> bff, final B z) { public V2 values() { return as; } + + @Override P3>, A, Option>> split1(final F predicate, final V acc) { + final Measured m = measured(); + final MakeTree mk = mkTree(m); + if (predicate.f(m.sum(acc, m.measure().f(as._1())))) { + return P.p(none(), as._1(), some(mk.one(as._2()))); + } else { + return P.p(some(mk.one(as._1())), as._2(), none()); + } + } + + @Override public P2 lookup(F o, int i) { + final F m = measured().measure(); + final int s1 = o.f(m.f(as._1())); + if (i < s1) { + return P.p(i, as._1()); + } else { + return P.p(i - s1, as._2()); + } + } + + @Override + 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 2d9be568..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,11 +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(new F2() { - public L f(final HNil hNil, final L l) { - return l; - } - }); + return new HAppend<>((hNil, l) -> l); } /** @@ -106,11 +100,7 @@ public L f(final HNil hNil, final L l) { */ public static , B, C extends HList, H extends HAppend> HAppend, B, HCons> append(final H h) { - return new HAppend, B, HCons>(new F2, B, HCons>() { - public HCons f(final HCons c, final B l) { - return cons(c.head(), h.append(c.tail(), l)); - } - }); + return new HAppend<>((c, l) -> cons(c.head(), h.append(c.tail(), l))); } } @@ -220,11 +210,7 @@ private HFoldr(final F3 foldRight) { * @return a fold instance for the empty list. */ public static HFoldr hFoldr() { - return new HFoldr(new F3() { - public V f(final G f, final V v, final HNil hNil) { - return v; - } - }); + return new HFoldr<>((f, v, hNil) -> v); } /** @@ -246,11 +232,7 @@ public V f(final G f, final V v, final HNil hNil) { H extends HFoldr, PP extends Apply, RR>> HFoldr, RR> hFoldr(final PP p, final H h) { - return new HFoldr, RR>(new F3, RR>() { - public RR f(final G f, final V v, final HCons c) { - return 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())))); } /** @@ -301,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 07d5ffa4..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 @@ -184,11 +183,7 @@ private HZero() { } public Show show() { - return Show.showS(new F() { - public String f(final HZero hZero) { - return "HZero"; - } - }); + return Show.showS(hZero -> "HZero"); } public Integer toInteger() { @@ -209,11 +204,7 @@ private HSucc(final N n) { private final N pred; public Show> show() { - return Show.showS(new F, String>() { - public String f(final HSucc s) { - return "HSucc (" + s.show().showS(s) + ')'; - } - }); + return Show.showS(s -> "HSucc (" + s.show().showS(s) + ')'); } public Integer toInteger() { diff --git a/core/src/main/java/fj/data/optic/Fold.java b/core/src/main/java/fj/data/optic/Fold.java new file mode 100644 index 00000000..691c779c --- /dev/null +++ b/core/src/main/java/fj/data/optic/Fold.java @@ -0,0 +1,122 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.Monoid; +import fj.data.Either; +import fj.data.List; +import fj.data.Option; + +/** + * A {@link Fold} can be seen as a {@link Getter} with many targets or a weaker {@link PTraversal} which cannot modify its + * target. + * + * {@link Fold} is on the top of the Optic hierarchy which means that {@link Getter}, {@link PTraversal}, {@link POptional}, + * {@link PLens}, {@link PPrism} and {@link PIso} are valid {@link Fold} + * + * @param the source of a {@link Fold} + * @param the target of a {@link Fold} + */ +public abstract class Fold { + + /** + * map each target to a {@link Monoid} and combine the results underlying representation of {@link Fold}, all {@link Fold} + * methods are defined in terms of foldMap + */ + public abstract F foldMap(Monoid m, F f); + + /** combine all targets using a target's {@link Monoid} */ + public final F fold(final Monoid m) { + return foldMap(m, Function.identity()); + } + + /** + * get all the targets of a {@link Fold} TODO: Shall it return a Stream as there might be an infinite number of targets? + */ + public final List getAll(final S s) { + return foldMap(Monoid.listMonoid(), List::single).f(s); + } + + /** find the first target of a {@link Fold} matching the predicate */ + public final F> find(final F p) { + return foldMap(Monoid.firstOptionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); + } + + /** get the first target of a {@link Fold} */ + public final Option headOption(final S s) { + return find(Function.constant(Boolean.TRUE)).f(s); + } + + /** check if at least one target satisfies the predicate */ + public final F exist(final F p) { + return foldMap(Monoid.disjunctionMonoid, p); + } + + /** check if all targets satisfy the predicate */ + public final F all(final F p) { + return foldMap(Monoid.conjunctionMonoid, p); + } + + /** join two {@link Fold} with the same target */ + public final Fold, A> sum(final Fold other) { + return new Fold, A>() { + @Override + public F, B> foldMap(final Monoid m, final F f) { + return s -> s.either(Fold.this.foldMap(m, f), other.foldMap(m, f)); + } + }; + } + + /**********************************************************/ + /** Compose methods between a {@link Fold} and another Optics */ + /**********************************************************/ + + /** compose a {@link Fold} with a {@link Fold} */ + 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)); + } + }; + } + + /** compose a {@link Fold} with a {@link Getter} */ + public final Fold composeGetter(final Getter other) { + return composeFold(other.asFold()); + } + + /** compose a {@link Fold} with a {@link POptional} */ + public final Fold composeOptional(final POptional other) { + return composeFold(other.asFold()); + } + + /** compose a {@link Fold} with a {@link PPrism} */ + public final Fold composePrism(final PPrism other) { + return composeFold(other.asFold()); + } + + /** compose a {@link Fold} with a {@link PLens} */ + public final Fold composeLens(final PLens other) { + return composeFold(other.asFold()); + } + + /** compose a {@link Fold} with a {@link PIso} */ + public final Fold composeIso(final PIso other) { + return composeFold(other.asFold()); + } + + public static Fold id() { + return PIso. pId().asFold(); + } + + public static Fold, A> codiagonal() { + return new Fold, A>() { + @Override + public F, B> foldMap(final Monoid m, final F f) { + return e -> e.either(f, f); + } + }; + } + +} diff --git a/core/src/main/java/fj/data/optic/Getter.java b/core/src/main/java/fj/data/optic/Getter.java new file mode 100644 index 00000000..a5c9fa99 --- /dev/null +++ b/core/src/main/java/fj/data/optic/Getter.java @@ -0,0 +1,110 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.Monoid; +import fj.P; +import fj.P2; +import fj.data.Either; + +/** + * A {@link Getter} can be seen as a glorified get method between a type S and a type A. + * + * A {@link Getter} is also a valid {@link Fold} + * + * @param the source of a {@link Getter} + * @param the target of a {@link Getter} + */ +public abstract class Getter { + + Getter() { + super(); + } + + /** get the target of a {@link Getter} */ + public abstract A get(S s); + + /** join two {@link Getter} with the same target */ + public final Getter, A> sum(final Getter other) { + return getter(e -> e.either(this::get, other::get)); + } + + /** pair two disjoint {@link Getter} */ + public final Getter, P2> product(final Getter other) { + return getter(p2 -> P.p(this.get(p2._1()), other.get(p2._2()))); + } + + public final Getter, P2> first() { + return getter(p -> P.p(this.get(p._1()), p._2())); + } + + public final Getter, P2> second() { + return getter(p -> P.p(p._1(), this.get(p._2()))); + } + + /*************************************************************/ + /** Compose methods between a {@link Getter} and another Optics */ + /*************************************************************/ + + /** compose a {@link Getter} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + /** compose a {@link Getter} with a {@link Getter} */ + public final Getter composeGetter(final Getter other) { + return getter(s -> other.get(get(s))); + } + + /** compose a {@link Getter} with a {@link POptional} */ + public final Fold composeOptional(final POptional other) { + return asFold().composeOptional(other); + } + + /** compose a {@link Getter} with a {@link PPrism} */ + public final Fold composePrism(final PPrism other) { + return asFold().composePrism(other); + } + + /** compose a {@link Getter} with a {@link PLens} */ + public final Getter composeLens(final PLens other) { + return composeGetter(other.asGetter()); + } + + /** compose a {@link Getter} with a {@link PIso} */ + public final Getter composeIso(final PIso other) { + return composeGetter(other.asGetter()); + } + + /******************************************************************/ + /** Transformation methods to view a {@link Getter} as another Optics */ + /******************************************************************/ + + /** view a {@link Getter} with a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid m, final F f) { + return s -> f.f(get(s)); + } + }; + } + + public static Getter id() { + return PIso. pId().asGetter(); + } + + public static Getter, A> codiagonal() { + return getter(e -> e.either(Function.identity(), Function.identity())); + } + + public static Getter getter(final F get) { + return new Getter() { + + @Override + public A get(final S s) { + return get.f(s); + } + }; + } +} diff --git a/core/src/main/java/fj/data/optic/Iso.java b/core/src/main/java/fj/data/optic/Iso.java new file mode 100644 index 00000000..4b41b1ad --- /dev/null +++ b/core/src/main/java/fj/data/optic/Iso.java @@ -0,0 +1,133 @@ +package fj.data.optic; + +import fj.F; +import fj.P2; + +/** {@link PIso} when S = T and A = B */ +public final class Iso extends PIso { + + final PIso pIso; + + public Iso(final PIso pIso) { + this.pIso = pIso; + } + + @Override + public A get(final S s) { + return pIso.get(s); + } + + @Override + public S reverseGet(final A a) { + return pIso.reverseGet(a); + } + + @Override + public Iso reverse() { + return new Iso<>(pIso.reverse()); + } + + /** pair two disjoint {@link Iso} */ + public Iso, P2> product(final Iso other) { + return new Iso<>(pIso.product(other.pIso)); + } + + @Override + public Iso, P2> first() { + return new Iso<>(pIso.first()); + } + + @Override + public Iso, P2> second() { + return new Iso<>(pIso.second()); + } + + /**********************************************************/ + /** Compose methods between an {@link Iso} and another Optics */ + /**********************************************************/ + + /** compose an {@link Iso} with a {@link Setter} */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pIso.composeSetter(other.pSetter)); + } + + /** compose an {@link Iso} with a {@link Traversal} */ + public Traversal composeTraversal(final Traversal other) { + return new Traversal<>(pIso.composeTraversal(other.pTraversal)); + } + + /** compose an {@link Iso} with a {@link Optional} */ + public Optional composeOptional(final Optional other) { + return new Optional<>(pIso.composeOptional(other.pOptional)); + } + + /** compose an {@link Iso} with a {@link Prism} */ + public Prism composePrism(final Prism other) { + return new Prism<>(pIso.composePrism(other.pPrism)); + } + + /** compose an {@link Iso} with a {@link Lens} */ + public Lens composeLens(final Lens other) { + return asLens().composeLens(other); + } + + /** compose an {@link Iso} with an {@link Iso} */ + public Iso composeIso(final Iso other) { + return new Iso<>(pIso.composeIso(other.pIso)); + } + + /****************************************************************/ + /** Transformation methods to view an {@link Iso} as another Optics */ + /****************************************************************/ + + /** view an {@link Iso} as a {@link Setter} */ + @Override + public Setter asSetter() { + return new Setter<>(pIso.asSetter()); + } + + /** view an {@link Iso} as a {@link Traversal} */ + @Override + public Traversal asTraversal() { + return new Traversal<>(pIso.asTraversal()); + } + + /** view an {@link Iso} as a {@link Optional} */ + @Override + public Optional asOptional() { + return new Optional<>(pIso.asOptional()); + } + + /** view an {@link Iso} as a {@link Prism} */ + @Override + public Prism asPrism() { + return new Prism<>(pIso.asPrism()); + } + + /** view an {@link Iso} as a {@link Lens} */ + @Override + public Lens asLens() { + return new Lens<>(pIso.asLens()); + } + + /** create an {@link Iso} using a pair of functions: one to get the target and one to get the source. */ + public static Iso iso(final F get, final F reverseGet) { + return new Iso<>(PIso.pIso(get, 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, ...) + */ + public static Iso id() { + return new Iso<>(PIso.pId()); + } + +} diff --git a/core/src/main/java/fj/data/optic/Lens.java b/core/src/main/java/fj/data/optic/Lens.java new file mode 100644 index 00000000..ceea42c9 --- /dev/null +++ b/core/src/main/java/fj/data/optic/Lens.java @@ -0,0 +1,172 @@ +package fj.data.optic; + +import fj.F; +import fj.P1; +import fj.control.Trampoline; +import fj.control.parallel.Promise; +import fj.data.Either; +import fj.data.IO; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * {@link PLens} with a monomorphic set function + */ +public final class Lens extends PLens { + + final PLens pLens; + + public Lens(final PLens pLens) { + this.pLens = pLens; + } + + @Override + public A get(final S s) { + return pLens.get(s); + } + + @Override + public F set(final A a) { + return pLens.set(a); + } + + @Override + public F> modifyFunctionF(final F> f) { + return pLens.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return pLens.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return pLens.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return pLens.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return pLens.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return pLens.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return pLens.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return pLens.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return pLens.modifyP1F(f); + } + + @Override + public F> modifyValidationF(final F> f) { + return pLens.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return pLens.modifyV2F(f); + } + + @Override + public F modify(final F f) { + return pLens.modify(f); + } + + /** join two {@link Lens} with the same target */ + public Lens, A> sum(final Lens other) { + return new Lens<>(pLens.sum(other.pLens)); + } + + /**********************************************************/ + /** Compose methods between a {@link Lens} and another Optics */ + /**********************************************************/ + + /** + * compose a {@link Lens} with a {@link Setter} + */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pLens.composeSetter(other.pSetter)); + } + + /** + * compose a {@link Lens} with a {@link Traversal} + */ + public Traversal composeTraversal(final Traversal other) { + return new Traversal<>(pLens.composeTraversal(other.pTraversal)); + } + + /** compose a {@link Lens} with an {@link Optional} */ + public Optional composeOptional(final Optional other) { + return new Optional<>(pLens.composeOptional(other.pOptional)); + } + + /** compose a {@link Lens} with a {@link Prism} */ + public Optional composePrism(final Prism other) { + return new Optional<>(pLens.composePrism(other.pPrism)); + } + + /** compose a {@link Lens} with a {@link Lens} */ + public Lens composeLens(final Lens other) { + return new Lens<>(pLens.composeLens(other.pLens)); + } + + /** compose a {@link Lens} with an {@link Iso} */ + public Lens composeIso(final Iso other) { + return new Lens<>(pLens.composeIso(other.pIso)); + } + + /****************************************************************/ + /** Transformation methods to view a {@link Lens} as another Optics */ + /****************************************************************/ + + /** view a {@link Lens} as a {@link Setter} */ + @Override + public Setter asSetter() { + return new Setter<>(pLens.asSetter()); + } + + /** view a {@link Lens} as a {@link Traversal} */ + @Override + public Traversal asTraversal() { + return new Traversal<>(pLens.asTraversal()); + } + + /** view a {@link Lens} as an {@link Optional} */ + @Override + public Optional asOptional() { + return new Optional<>(pLens.asOptional()); + } + + public static Lens id() { + return new Lens<>(PLens.pId()); + } + + /** + * create a {@link Lens} using a pair of functions: one to get the target, one to set the target. + */ + public static Lens lens(final F get, final F> set) { + return new Lens<>(PLens.pLens(get, set)); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fj/data/optic/Optional.java b/core/src/main/java/fj/data/optic/Optional.java new file mode 100644 index 00000000..b2106cae --- /dev/null +++ b/core/src/main/java/fj/data/optic/Optional.java @@ -0,0 +1,292 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.P; +import fj.P1; +import fj.P2; +import fj.control.Trampoline; +import fj.control.parallel.Promise; +import fj.control.parallel.Strategy; +import fj.data.Either; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V; +import fj.data.vector.V2; + +/** {@link POptional} restricted to monomorphic update */ +public final class Optional extends POptional { + + final POptional pOptional; + + public Optional(final POptional pOptional) { + this.pOptional = pOptional; + } + + @Override + public F set(final A a) { + return pOptional.set(a); + } + + @Override + public F> modifyValidationF(final F> f) { + return pOptional.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return pOptional.modifyV2F(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return pOptional.modifyTrampolineF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return pOptional.modifyStreamF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return pOptional.modifyPromiseF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return pOptional.modifyP1F(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return pOptional.modifyOptionF(f); + } + + @Override + public F> modifyListF(final F> f) { + return pOptional.modifyListF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return pOptional.modifyIOF(f); + } + + @Override + public F> modifyFunctionF(final F> f) { + return pOptional.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return pOptional.modifyEitherF(f); + } + + @Override + public F modify(final F f) { + return pOptional.modify(f); + } + + @Override + public Either getOrModify(final S s) { + return pOptional.getOrModify(s); + } + + @Override + public Option getOption(final S s) { + return pOptional.getOption(s); + } + + /** join two {@link Optional} with the same target */ + public Optional, A> sum(final Optional other) { + return new Optional<>(pOptional.sum(other.pOptional)); + } + + @Override + public Optional, P2> first() { + return new Optional<>(pOptional.first()); + } + + @Override + public Optional, P2> second() { + return new Optional<>(pOptional.second()); + } + + /**************************************************************/ + /** Compose methods between a {@link Optional} and another Optics */ + /**************************************************************/ + + /** compose a {@link Optional} with a {@link Setter} */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pOptional.composeSetter(other.pSetter)); + } + + /** compose a {@link Optional} with a {@link Traversal} */ + public Traversal composeTraversal(final Traversal other) { + return new Traversal<>(pOptional.composeTraversal(other.pTraversal)); + } + + /** compose a {@link Optional} with a {@link Optional} */ + public Optional composeOptional(final Optional other) { + return new Optional<>(pOptional.composeOptional(other.pOptional)); + } + + /** compose a {@link Optional} with a {@link Prism} */ + public Optional composePrism(final Prism other) { + return new Optional<>(pOptional.composePrism(other.pPrism)); + } + + /** compose a {@link Optional} with a {@link Lens} */ + public Optional composeLens(final Lens other) { + return new Optional<>(pOptional.composeLens(other.pLens)); + } + + /** compose a {@link Optional} with an {@link Iso} */ + public Optional composeIso(final Iso other) { + return new Optional<>(pOptional.composeIso(other.pIso)); + } + + /********************************************************************/ + /** Transformation methods to view a {@link Optional} as another Optics */ + /********************************************************************/ + + /** view a {@link Optional} as a {@link Setter} */ + @Override + public Setter asSetter() { + return new Setter<>(pOptional.asSetter()); + } + + /** view a {@link Optional} as a {@link Traversal} */ + @Override + public Traversal asTraversal() { + return new Traversal<>(pOptional.asTraversal()); + } + + 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_()); + } + + @Override + public F set(final A a) { + return set.f(a); + } + + @Override + public Option getOption(final S s) { + return getOption.f(s); + } + + @Override + public F> modifyFunctionF(final F> f) { + return s -> getOption.f(s).> option( + (C __) -> s, + a -> Function.compose(b -> set.f(b).f(s), f.f(a)) + ); + } + + @Override + public F> modifyEitherF(final F> f) { + return s -> getOption.f(s).> option( + Either.right(s), + t -> f.f(t).right().map(b -> set.f(b).f(s)) + ); + } + + @Override + 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)) + ); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return s -> getOption.f(s).option( + Trampoline.pure(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyPromiseF(final F> f) { + return s -> getOption.f(s).> option( + () -> Promise.promise(Strategy.idStrategy(), P.p(s)), + t -> f.f(t).fmap(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyListF(final F> f) { + return s -> getOption.f(s).> option( + () -> List.single(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyOptionF(final F> f) { + return s -> getOption.f(s).option( + Option.some(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyStreamF(final F> f) { + return s -> getOption.f(s).> option( + () -> Stream.single(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyP1F(final F> f) { + return s -> getOption.f(s).> option( + P.p(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyValidationF(final F> f) { + return s -> getOption.f(s).> option( + () -> Validation. success(s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyV2F(final F> f) { + return s -> getOption.f(s).> option( + () -> V.v(s, s), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F modify(final F f) { + return s -> getOption.f(s).option(s, a -> set.f(f.f(a)).f(s)); + } + + }); + } + +} diff --git a/core/src/main/java/fj/data/optic/PIso.java b/core/src/main/java/fj/data/optic/PIso.java new file mode 100644 index 00000000..b97b416f --- /dev/null +++ b/core/src/main/java/fj/data/optic/PIso.java @@ -0,0 +1,598 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.Monoid; +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; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * 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 {@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
+ *     |           |                                   |           |
+ *     |           |                                   |           |
+ * get |           | reverseGet     reverse.reverseGet |           | reverse.get
+ *     |           |                                   |           |
+ *     |     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} + * + * @param the source of a {@link PIso} + * @param the modified source of a {@link PIso} + * @param
the target of a {@link PIso} + * @param the modified target of a {@link PIso} + */ +public abstract class PIso { + + PIso() { + super(); + } + + /** get the target of a {@link PIso} */ + public abstract A get(S s); + + /** get the modified source of a {@link PIso} */ + public abstract T reverseGet(B b); + + /** reverse a {@link PIso}: the source becomes the target and the target becomes the source */ + public abstract PIso reverse(); + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyFunctionF(final F> f) { + return s -> Function.compose(this::reverseGet, f.f(get(s))); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyEitherF(final F> f) { + return s -> f.f(get(s)).right().map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyIOF(final F> f) { + return s -> IOFunctions.map(f.f(get(s)), this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyTrampolineF(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyPromiseF(final F> f) { + return s -> f.f(get(s)).fmap(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyListF(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyOptionF(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyStreamF(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyP1F(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyValidationF(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with an Applicative function */ + public final F> modifyV2F(final F> f) { + return s -> f.f(get(s)).map(this::reverseGet); + } + + /** modify polymorphically the target of a {@link PIso} with a function */ + public final F modify(final F f) { + return s -> reverseGet(f.f(get(s))); + } + + /** set polymorphically the target of a {@link PIso} with a value */ + public final F set(final B b) { + return Function.constant(reverseGet(b)); + } + + /** pair two disjoint {@link PIso} */ + 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()))); + } + + public PIso, P2, P2, P2> first() { + return pIso( + sc -> P.p(get(sc._1()), sc._2()), + bc -> P.p(reverseGet(bc._1()), bc._2())); + } + + public PIso, P2, P2, P2> second() { + return pIso( + cs -> P.p(cs._1(), get(cs._2())), + cb -> P.p(cb._1(), reverseGet(cb._2()))); + } + + /**********************************************************/ + /** Compose methods between a {@link PIso} and another Optics */ + /**********************************************************/ + + /** compose a {@link PIso} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + /** compose a {@link PIso} with a {@link Getter} */ + public final Getter composeGetter(final Getter other) { + return asGetter().composeGetter(other); + } + + /** compose a {@link PIso} with a {@link PSetter} */ + public final PSetter composeSetter(final PSetter other) { + return asSetter().composeSetter(other); + } + + /** compose a {@link PIso} with a {@link PTraversal} */ + public final PTraversal composeTraversal(final PTraversal other) { + return asTraversal().composeTraversal(other); + } + + /** compose a {@link PIso} with a {@link POptional} */ + public final POptional composeOptional(final POptional other) { + return asOptional().composeOptional(other); + } + + /** compose a {@link PIso} with a {@link PPrism} */ + public final PPrism composePrism(final PPrism other) { + return asPrism().composePrism(other); + } + + /** compose a {@link PIso} with a {@link PLens} */ + public final PLens composeLens(final PLens other) { + return asLens().composeLens(other); + } + + /** compose a {@link PIso} with a {@link PIso} */ + public final PIso composeIso(final PIso other) { + final PIso self = this; + return new PIso() { + @Override + public C get(final S s) { + return other.get(self.get(s)); + } + + @Override + public T reverseGet(final D d) { + return self.reverseGet(other.reverseGet(d)); + } + + @Override + public PIso reverse() { + final PIso composeSelf = this; + return new PIso() { + @Override + public T get(final D d) { + return self.reverseGet(other.reverseGet(d)); + } + + @Override + public C reverseGet(final S s) { + return other.get(self.get(s)); + } + + @Override + public PIso reverse() { + return composeSelf; + } + }; + } + }; + } + + /****************************************************************/ + /** Transformation methods to view a {@link PIso} as another Optics */ + /****************************************************************/ + + /** view a {@link PIso} as a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid m, final F f) { + return s -> f.f(PIso.this.get(s)); + } + }; + } + + /** view a {@link PIso} as a {@link Getter} */ + public final Getter asGetter() { + return new Getter() { + @Override + public A get(final S s) { + return PIso.this.get(s); + } + }; + } + + /** view a {@link PIso} as a {@link Setter} */ + public PSetter asSetter() { + return new PSetter() { + @Override + public F modify(final F f) { + return PIso.this.modify(f); + } + + @Override + public F set(final B b) { + return PIso.this.set(b); + } + }; + } + + /** view a {@link PIso} as a {@link PTraversal} */ + public PTraversal asTraversal() { + final PIso self = this; + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> f.f(self.get(s)); + } + + }; + } + + /** view a {@link PIso} as a {@link POptional} */ + public POptional asOptional() { + final PIso self = this; + return new POptional() { + @Override + public Either getOrModify(final S s) { + return Either.right(self.get(s)); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F set(final B b) { + return self.set(b); + } + + @Override + public Option getOption(final S s) { + return Option.some(self.get(s)); + } + + @Override + public F modify(final F f) { + return self.modify(f); + } + }; + } + + /** view a {@link PIso} as a {@link PPrism} */ + public PPrism asPrism() { + final PIso self = this; + return new PPrism() { + @Override + public Either getOrModify(final S s) { + return Either.right(self.get(s)); + } + + @Override + public T reverseGet(final B b) { + return self.reverseGet(b); + } + + @Override + public Option getOption(final S s) { + return Option.some(self.get(s)); + } + }; + } + + /** view a {@link PIso} as a {@link PLens} */ + public PLens asLens() { + final PIso self = this; + return new PLens() { + @Override + public A get(final S s) { + return self.get(s); + } + + @Override + public F set(final B b) { + return self.set(b); + } + + @Override + public F modify(final F f) { + return self.modify(f); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + }; + } + + /** create a {@link PIso} using a pair of functions: one to get the target and one to get the source. */ + public static PIso pIso(final F get, final F reverseGet) { + return new PIso() { + + @Override + public A get(final S s) { + return get.f(s); + } + + @Override + public T reverseGet(final B b) { + return reverseGet.f(b); + } + + @Override + public PIso reverse() { + final PIso self = this; + return new PIso() { + @Override + public T get(final B b) { + return reverseGet.f(b); + } + + @Override + public A reverseGet(final S s) { + return get.f(s); + } + + @Override + public PIso reverse() { + return self; + } + }; + } + + }; + } + + /** + * 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, ...) + */ + public static PIso pId() { + return new PIso() { + + @Override + public S get(final S s) { + return s; + } + + @Override + public T reverseGet(final T t) { + return t; + } + + @Override + public PIso reverse() { + final PIso self = this; + return new PIso() { + @Override + public T get(final T t) { + return t; + } + + @Override + public S reverseGet(final S s) { + return s; + } + + @Override + public PIso reverse() { + return self; + } + }; + } + }; + } + +} diff --git a/core/src/main/java/fj/data/optic/PLens.java b/core/src/main/java/fj/data/optic/PLens.java new file mode 100644 index 00000000..4dcad20b --- /dev/null +++ b/core/src/main/java/fj/data/optic/PLens.java @@ -0,0 +1,505 @@ +package fj.data.optic; + +import fj.F; +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; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * 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.

+ * + * @param the source of a {@link PLens} + * @param the modified source of a {@link PLens} + * @param
the target of a {@link PLens} + * @param the modified target of a {@link PLens} + */ +public abstract class PLens { + + PLens() { + super(); + } + + /** get the target of a {@link PLens} */ + public abstract A get(S s); + + /** set polymorphically the target of a {@link PLens} using a function */ + public abstract F set(B b); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyFunctionF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyEitherF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyIOF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyTrampolineF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyPromiseF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyListF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyOptionF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyStreamF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyP1F(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyValidationF(F> f); + + /** + * modify polymorphically the target of a {@link PLens} with an Applicative function + */ + public abstract F> modifyV2F(F> f); + + /** modify polymorphically the target of a {@link PLens} using a function */ + public abstract F modify(final F f); + + /** join two {@link PLens} with the same target */ + public final PLens, Either, A, B> sum(final PLens other) { + return pLens( + e -> e.either(this::get, other::get), + b -> e -> e.bimap(PLens.this.set(b), other.set(b))); + } + + /***********************************************************/ + /** Compose methods between a {@link PLens} and another Optics */ + /***********************************************************/ + + /** compose a {@link PLens} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + /** compose a {@link PLens} with a {@link Getter} */ + public final Getter composeGetter(final Getter other) { + return asGetter().composeGetter(other); + } + + /** + * compose a {@link PLens} with a {@link PSetter} + */ + public final PSetter composeSetter(final PSetter other) { + return asSetter().composeSetter(other); + } + + /** + * compose a {@link PLens} with a {@link PTraversal} + */ + public final PTraversal composeTraversal(final PTraversal other) { + return asTraversal().composeTraversal(other); + } + + /** compose a {@link PLens} with an {@link POptional} */ + public final POptional composeOptional(final POptional other) { + return asOptional().composeOptional(other); + } + + /** compose a {@link PLens} with a {@link PPrism} */ + public final POptional composePrism(final PPrism other) { + return asOptional().composeOptional(other.asOptional()); + } + + /** compose a {@link PLens} with a {@link PLens} */ + public final PLens composeLens(final PLens other) { + final PLens self = this; + return new PLens() { + @Override + public C get(final S s) { + return other.get(self.get(s)); + } + + @Override + public F set(final D d) { + return self.modify(other.set(d)); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(other.modifyFunctionF(f)); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(other.modifyEitherF(f)); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(other.modifyIOF(f)); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(other.modifyTrampolineF(f)); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(other.modifyPromiseF(f)); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(other.modifyListF(f)); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(other.modifyOptionF(f)); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(other.modifyStreamF(f)); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(other.modifyP1F(f)); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(other.modifyValidationF(f)); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(other.modifyV2F(f)); + } + + @Override + public F modify(final F f) { + return self.modify(other.modify(f)); + } + }; + } + + /** compose a {@link PLens} with an {@link PIso} */ + public final PLens composeIso(final PIso other) { + return composeLens(other.asLens()); + } + + /************************************************************************************************/ + /** Transformation methods to view a {@link PLens} as another Optics */ + /************************************************************************************************/ + + /** view a {@link PLens} as a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid m, final F f) { + return s -> f.f(get(s)); + } + }; + } + + /** view a {@link PLens} as a {@link Getter} */ + public final Getter asGetter() { + return new Getter() { + @Override + public A get(final S s) { + return PLens.this.get(s); + } + }; + } + + /** view a {@link PLens} as a {@link PSetter} */ + public PSetter asSetter() { + return new PSetter() { + @Override + public F modify(final F f) { + return PLens.this.modify(f); + } + + @Override + public F set(final B b) { + return PLens.this.set(b); + } + }; + } + + /** view a {@link PLens} as a {@link PTraversal} */ + public PTraversal asTraversal() { + final PLens self = this; + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> f.f(get(s)); + } + + }; + } + + /** view a {@link PLens} as an {@link POptional} */ + public POptional asOptional() { + final PLens self = this; + return new POptional() { + @Override + public Either getOrModify(final S s) { + return Either.right(self.get(s)); + } + + @Override + public F set(final B b) { + return self.set(b); + } + + @Override + public Option getOption(final S s) { + return Option.some(self.get(s)); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F modify(final F f) { + return self.modify(f); + } + }; + } + + public static PLens pId() { + return PIso. pId().asLens(); + } + + /** + * create a {@link PLens} using a pair of functions: one to get the target, one to set the target. + */ + public static PLens pLens(final F get, final F> set) { + return new PLens() { + + @Override + public A get(final S s) { + return get.f(s); + } + + @Override + public F set(final B b) { + return set.f(b); + } + + @Override + public F> modifyFunctionF(final F> f) { + return s -> Function.compose(b -> set.f(b).f(s), f.f(get.f(s))); + } + + @Override + public F> modifyEitherF(final F> f) { + return s -> f.f(get.f(s)).right().map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyIOF(final F> f) { + return s -> IOFunctions.map(f.f(get.f(s)), a -> set.f(a).f(s)); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyPromiseF(final F> f) { + return s -> f.f(get.f(s)).fmap(a -> set.f(a).f(s)); + } + + @Override + public F> modifyListF(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyOptionF(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyStreamF(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyP1F(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyValidationF(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F> modifyV2F(final F> f) { + return s -> f.f(get.f(s)).map(a -> set.f(a).f(s)); + } + + @Override + public F modify(final F f) { + return s -> set.f(f.f(get.f(s))).f(s); + } + }; + } +} diff --git a/core/src/main/java/fj/data/optic/POptional.java b/core/src/main/java/fj/data/optic/POptional.java new file mode 100644 index 00000000..c70aa4c1 --- /dev/null +++ b/core/src/main/java/fj/data/optic/POptional.java @@ -0,0 +1,484 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.Monoid; +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; +import fj.data.Either; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * 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} + * @param
the target of a {@link POptional} + * @param the modified target of a {@link POptional} + */ +public abstract class POptional { + + POptional() { + super(); + } + + /** get the target of a {@link POptional} or modify the source in case there is no target */ + public abstract Either getOrModify(S s); + + /** get the modified source of a {@link POptional} */ + public abstract F set(final B b); + + /** get the target of a {@link POptional} or nothing if there is no target */ + public abstract Option getOption(final S s); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyFunctionF(final F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyEitherF(final F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyIOF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyTrampolineF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyPromiseF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyListF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyOptionF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyStreamF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyP1F(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyValidationF(F> f); + + /** + * modify polymorphically the target of a {@link POptional} with an Applicative function + */ + public abstract F> modifyV2F(F> f); + + /** modify polymorphically the target of a {@link POptional} with a function */ + public abstract F modify(final F f); + + /** + * modify polymorphically the target of a {@link POptional} with a function. return empty if the {@link POptional} is not + * matching + */ + public final F> modifyOption(final F f) { + return s -> getOption(s).map(Function.constant(modify(f).f(s))); + } + + /** set polymorphically the target of a {@link POptional} with a value. return empty if the {@link POptional} is not matching */ + public final F> setOption(final B b) { + return modifyOption(Function.constant(b)); + } + + /** check if a {@link POptional} has a target */ + public final boolean isMatching(final S s) { + return getOption(s).isSome(); + + } + + /** join two {@link POptional} with the same target */ + public final POptional, Either, A, B> sum(final POptional other) { + return pOptional( + e -> e.either(s -> getOrModify(s).left().map(Either.left_()), s1 -> other.getOrModify(s1).left().map(Either.right_())), + b -> e -> e.bimap(set(b), other.set(b))); + } + + public POptional, P2, P2, P2> first() { + return pOptional( + sc -> getOrModify(sc._1()).bimap(t -> P.p(t, sc._2()), a -> P.p(a, sc._2())), + bc -> s_ -> P.p(set(bc._1()).f(s_._1()), bc._2())); + } + + public POptional, P2, P2, P2> second() { + return pOptional( + cs -> getOrModify(cs._2()).bimap(t -> P.p(cs._1(), t), a -> P.p(cs._1(), a)), + cb -> _s -> P.p(cb._1(), set(cb._2()).f(_s._2()))); + } + + /***************************************************************/ + /** Compose methods between a {@link POptional} and another Optics */ + /***************************************************************/ + + /** compose a {@link POptional} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + /** compose a {@link POptional} with a {@link Getter} */ + public final Fold composeGetter(final Getter other) { + return asFold().composeGetter(other); + } + + /** compose a {@link POptional} with a {@link PSetter} */ + public final PSetter composeSetter(final PSetter other) { + return asSetter().composeSetter(other); + } + + /** compose a {@link POptional} with a {@link PTraversal} */ + public final PTraversal composeTraversal(final PTraversal other) { + return asTraversal().composeTraversal(other); + } + + /** compose a {@link POptional} with a {@link POptional} */ + public final POptional composeOptional(final POptional other) { + final POptional self = this; + return new POptional() { + + @Override + public Either getOrModify(final S s) { + return self.getOrModify(s).right() + .bind(a -> other.getOrModify(a).bimap(b -> POptional.this.set(b).f(s), Function.identity())); + } + + @Override + public F set(final D d) { + return self.modify(other.set(d)); + } + + @Override + public Option getOption(final S s) { + return self.getOption(s).bind(other::getOption); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(other.modifyFunctionF(f)); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(other.modifyEitherF(f)); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(other.modifyIOF(f)); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(other.modifyTrampolineF(f)); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(other.modifyPromiseF(f)); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(other.modifyListF(f)); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(other.modifyOptionF(f)); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(other.modifyStreamF(f)); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(other.modifyP1F(f)); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(other.modifyValidationF(f)); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(other.modifyV2F(f)); + } + + @Override + public F modify(final F f) { + return self.modify(other.modify(f)); + } + }; + } + + /** compose a {@link POptional} with a {@link PPrism} */ + public final POptional composePrism(final PPrism other) { + return composeOptional(other.asOptional()); + } + + /** compose a {@link POptional} with a {@link PLens} */ + public final POptional composeLens(final PLens other) { + return composeOptional(other.asOptional()); + } + + /** compose a {@link POptional} with a {@link PIso} */ + public final POptional composeIso(final PIso other) { + return composeOptional(other.asOptional()); + } + + /*********************************************************************/ + /** Transformation methods to view a {@link POptional} as another Optics */ + /*********************************************************************/ + + /** view a {@link POptional} as a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid m, final F f) { + return s -> POptional.this.getOption(s).map(f).orSome(m.zero()); + } + }; + } + + /** view a {@link POptional} as a {@link PSetter} */ + public PSetter asSetter() { + return new PSetter() { + @Override + public F modify(final F f) { + return POptional.this.modify(f); + } + + @Override + public F set(final B b) { + return POptional.this.set(b); + } + }; + } + + /** view a {@link POptional} as a {@link PTraversal} */ + public PTraversal asTraversal() { + final POptional self = this; + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> self.getOption(s).map(f).orSome(monoid.zero()); + } + }; + } + + public static POptional pId() { + return PIso. pId().asOptional(); + } + + /** create a {@link POptional} using the canonical functions: getOrModify and set */ + public static POptional pOptional(final F> getOrModify, final F> set) { + return new POptional() { + @Override + public Either getOrModify(final S s) { + return getOrModify.f(s); + } + + @Override + public F set(final B b) { + return set.f(b); + } + + @Override + public Option getOption(final S s) { + return getOrModify.f(s).right().toOption(); + } + + @Override + public F> modifyFunctionF(final F> f) { + return s -> getOrModify.f(s).either( + Function.constant(), + a -> Function.compose(b -> set.f(b).f(s), f.f(a)) + ); + } + + @Override + public F> modifyEitherF(final F> f) { + return s -> getOrModify.f(s).either( + Either.right_(), + t -> f.f(t).right().map(b -> set.f(b).f(s)) + ); + } + + @Override + 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)) + ); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return s -> getOrModify.f(s).either( + Trampoline.pure(), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyPromiseF(final F> f) { + return s -> getOrModify.f(s).either( + t -> Promise.promise(Strategy.idStrategy(), P.p(t)), + t -> f.f(t).fmap(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyListF(final F> f) { + return s -> getOrModify.f(s).either( + List::single, + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyOptionF(final F> f) { + return s -> getOrModify.f(s).either( + Option.some_(), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyStreamF(final F> f) { + return s -> getOrModify.f(s).either( + Stream.single(), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyP1F(final F> f) { + return s -> getOrModify.f(s).either( + P.p1(), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyValidationF(final F> f) { + return s -> getOrModify.f(s).either( + Validation::success, + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F> modifyV2F(final F> f) { + return s -> getOrModify.f(s).either( + t -> V2.p(P.p(t, t)), + t -> f.f(t).map(b -> set.f(b).f(s)) + ); + } + + @Override + public F modify(final F f) { + return s -> getOrModify.f(s).either(Function.identity(), a -> set.f(f.f(a)).f(s)); + } + }; + } + +} diff --git a/core/src/main/java/fj/data/optic/PPrism.java b/core/src/main/java/fj/data/optic/PPrism.java new file mode 100644 index 00000000..ffa87ba6 --- /dev/null +++ b/core/src/main/java/fj/data/optic/PPrism.java @@ -0,0 +1,442 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +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; +import fj.data.Either; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * 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.

+ * + * {@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} + * + * @param the source of a {@link PPrism} + * @param the modified source of a {@link PPrism} + * @param
the target of a {@link PPrism} + * @param the modified target of a {@link PPrism} + */ +public abstract class PPrism { + + PPrism() { + super(); + } + + /** get the target of a {@link PPrism} or modify the source in case there is no target */ + + public abstract Either getOrModify(S s); + + /** get the modified source of a {@link PPrism} */ + public abstract T reverseGet(B b); + + /** get the target of a {@link PPrism} or nothing if there is no target */ + public abstract Option getOption(final S s); + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyFunctionF(final F> f) { + return s -> getOrModify(s).either( + Function.constant(), + a -> Function.compose(this::reverseGet, f.f(a)) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyEitherF(final F> f) { + return s -> getOrModify(s).either( + Either.right_(), + t -> f.f(t).right().map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyIOF(final F> f) { + return s -> getOrModify(s).either( + IOFunctions::unit, + t -> IOFunctions.map(f.f(t), this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyTrampolineF(final F> f) { + return s -> getOrModify(s).either( + Trampoline.pure(), + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyPromiseF(final F> f) { + return s -> getOrModify(s).either( + t -> Promise.promise(Strategy.idStrategy(), P.p(t)), + t -> f.f(t).fmap(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyListF(final F> f) { + return s -> getOrModify(s).either( + List::single, + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyOptionF(final F> f) { + return s -> getOrModify(s).either( + Option.some_(), + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyStreamF(final F> f) { + return s -> getOrModify(s).either( + Stream.single(), + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyP1F(final F> f) { + return s -> getOrModify(s).either( + P.p1(), + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyValidationF(final F> f) { + return s -> getOrModify(s).either( + Validation::success, + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with an Applicative function */ + public final F> modifyV2F(final F> f) { + return s -> getOrModify(s).either( + t -> V2.p(P.p(t, t)), + t -> f.f(t).map(this::reverseGet) + ); + } + + /** modify polymorphically the target of a {@link PPrism} with a function */ + public final F modify(final F f) { + return s -> getOrModify(s).either(Function.identity(), a -> reverseGet(f.f(a))); + } + + /** modify polymorphically the target of a {@link PPrism} with a function. return empty if the {@link PPrism} is not matching */ + public final F> modifyOption(final F f) { + return s -> getOption(s).map(a -> reverseGet(f.f(a))); + } + + /** set polymorphically the target of a {@link PPrism} with a value */ + public final F set(final B b) { + return modify(Function.constant(b)); + } + + /** set polymorphically the target of a {@link PPrism} with a value. return empty if the {@link PPrism} is not matching */ + public final F> setOption(final B b) { + return modifyOption(Function.constant(b)); + } + + /** check if a {@link PPrism} has a target */ + public final boolean isMatching(final S s) { + return getOption(s).isSome(); + } + + /** create a {@link Getter} from the modified target to the modified source of a {@link PPrism} */ + public final Getter re() { + return Getter.getter(this::reverseGet); + } + + /************************************************************/ + /** Compose methods between a {@link PPrism} and another Optics */ + /************************************************************/ + + /** compose a {@link PPrism} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + /** compose a {@link PPrism} with a {@link Getter} */ + public final Fold composeGetter(final Getter other) { + return asFold().composeGetter(other); + } + + /** compose a {@link PPrism} with a {@link PSetter} */ + public final PSetter composeSetter(final PSetter other) { + return asSetter().composeSetter(other); + } + + /** compose a {@link PPrism} with a {@link PTraversal} */ + public final PTraversal composeTraversal(final PTraversal other) { + return asTraversal().composeTraversal(other); + } + + /** compose a {@link PPrism} with a {@link POptional} */ + public final POptional composeOptional(final POptional other) { + return asOptional().composeOptional(other); + } + + /** compose a {@link PPrism} with a {@link PLens} */ + public final POptional composeLens(final PLens other) { + return asOptional().composeOptional(other.asOptional()); + } + + /** compose a {@link PPrism} with a {@link PPrism} */ + public final PPrism composePrism(final PPrism other) { + return new PPrism() { + + @Override + public Either getOrModify(final S s) { + return PPrism.this.getOrModify(s).right() + .bind(a -> other.getOrModify(a).bimap(b -> PPrism.this.set(b).f(s), Function.identity())); + } + + @Override + public T reverseGet(final D d) { + return PPrism.this.reverseGet(other.reverseGet(d)); + } + + @Override + public Option getOption(final S s) { + return PPrism.this.getOption(s).bind(other::getOption); + } + }; + } + + /** compose a {@link PPrism} with a {@link PIso} */ + public final PPrism composeIso(final PIso other) { + return composePrism(other.asPrism()); + } + + /******************************************************************/ + /** Transformation methods to view a {@link PPrism} as another Optics */ + /******************************************************************/ + + /** view a {@link PPrism} as a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> getOption(s).map(f).orSome(monoid.zero()); + } + }; + } + + /** view a {@link PPrism} as a {@link Setter} */ + public PSetter asSetter() { + return new PSetter() { + @Override + public F modify(final F f) { + return PPrism.this.modify(f); + } + + @Override + public F set(final B b) { + return PPrism.this.set(b); + } + }; + } + + /** view a {@link PPrism} as a {@link PTraversal} */ + public PTraversal asTraversal() { + final PPrism self = this; + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> getOption(s).map(f).orSome(monoid.zero()); + } + + }; + } + + /** view a {@link PPrism} as a {@link POptional} */ + public POptional asOptional() { + final PPrism self = this; + return new POptional() { + + @Override + public Either getOrModify(final S s) { + return self.getOrModify(s); + } + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(f); + } + + @Override + public F> modifyValidationF(final F> f) { + return self.modifyValidationF(f); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(f); + } + + @Override + public F set(final B b) { + return self.set(b); + } + + @Override + public Option getOption(final S s) { + return self.getOption(s); + } + + @Override + public F modify(final F f) { + return self.modify(f); + } + + }; + } + + public static PPrism pId() { + return PIso. pId().asPrism(); + } + + /** create a {@link PPrism} using the canonical functions: getOrModify and reverseGet */ + public static PPrism pPrism(final F> getOrModify, final F reverseGet) { + return new PPrism() { + + @Override + public Either getOrModify(final S s) { + return getOrModify.f(s); + } + + @Override + public T reverseGet(final B b) { + return reverseGet.f(b); + } + + @Override + public Option getOption(final S s) { + return getOrModify.f(s).right().toOption(); + } + }; + } + +} diff --git a/core/src/main/java/fj/data/optic/PSetter.java b/core/src/main/java/fj/data/optic/PSetter.java new file mode 100644 index 00000000..13be7739 --- /dev/null +++ b/core/src/main/java/fj/data/optic/PSetter.java @@ -0,0 +1,106 @@ +package fj.data.optic; + +import fj.F; +import fj.Function; +import fj.data.Either; + +/** + * 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} + * @param
the target of a {@link PSetter} + * @param the modified target of a {@link PSetter} + */ +public abstract class PSetter { + + PSetter() { + super(); + } + + /** modify polymorphically the target of a {@link PSetter} with a function */ + public abstract F modify(F f); + + /** set polymorphically the target of a {@link PSetter} with a value */ + public abstract F set(final B b); + + /** join two {@link PSetter} with the same target */ + public final PSetter, Either, A, B> sum(final PSetter other) { + return pSetter(f -> e -> e.bimap(modify(f), other.modify(f))); + } + + /*************************************************************/ + /** Compose methods between a {@link PSetter} and another Optics */ + /*************************************************************/ + + /** compose a {@link PSetter} with a {@link PSetter} */ + public final PSetter composeSetter(final PSetter other) { + final PSetter self = this; + return new PSetter() { + + @Override + public F modify(final F f) { + return self.modify(other.modify(f)); + } + + @Override + public F set(final D d) { + return self.modify(other.set(d)); + } + }; + } + + /** compose a {@link PSetter} with a {@link PTraversal} */ + public final PSetter composeTraversal(final PTraversal other) { + return composeSetter(other.asSetter()); + } + + /** compose a {@link PSetter} with a {@link POptional} */ + public final PSetter composeOptional(final POptional other) { + return composeSetter(other.asSetter()); + } + + /** compose a {@link PSetter} with a {@link PPrism} */ + public final PSetter composePrism(final PPrism other) { + return composeSetter(other.asSetter()); + } + + /** compose a {@link PSetter} with a {@link PLens} */ + public final PSetter composeLens(final PLens other) { + return composeSetter(other.asSetter()); + } + + /** compose a {@link PSetter} with a {@link PIso} */ + public final PSetter composeIso(final PIso other) { + return composeSetter(other.asSetter()); + } + + public static PSetter pId() { + return PIso. pId().asSetter(); + } + + public static PSetter, Either, S, T> pCodiagonal() { + return pSetter(f -> e -> e.bimap(f, f)); + } + + public static PSetter pSetter(final F, F> modify) { + return new PSetter() { + @Override + public F modify(final F f) { + return modify.f(f); + } + + @Override + public F set(final B b) { + return modify.f(Function.constant(b)); + } + }; + } +} diff --git a/core/src/main/java/fj/data/optic/PTraversal.java b/core/src/main/java/fj/data/optic/PTraversal.java new file mode 100644 index 00000000..6087f707 --- /dev/null +++ b/core/src/main/java/fj/data/optic/PTraversal.java @@ -0,0 +1,625 @@ +package fj.data.optic; + +import fj.F; +import fj.F3; +import fj.F4; +import fj.F5; +import fj.F6; +import fj.F7; +import fj.Function; +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; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +/** + * 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 {@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} + * @param
the target of a {@link PTraversal} + * @param the modified target of a {@link PTraversal} + */ +public abstract class PTraversal { + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyFunctionF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyEitherF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyIOF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyTrampolineF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyPromiseF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyListF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyOptionF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyStreamF(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyP1F(F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyValidationF(Semigroup s, F> f); + + /** + * modify polymorphically the target of a {@link PTraversal} with an Applicative function + */ + public abstract F> modifyV2F(F> f); + + /** map each target to a {@link Monoid} and combine the results */ + public abstract F foldMap(Monoid monoid, F f); + + /** combine all targets using a target's {@link Monoid} */ + public final F fold(final Monoid m) { + return foldMap(m, Function.identity()); + } + + /** get all the targets of a {@link PTraversal} */ + public final List getAll(final S s) { + return foldMap(Monoid.listMonoid(), List::single).f(s); + } + + /** find the first target of a {@link PTraversal} matching the predicate */ + public final F> find(final F p) { + return foldMap(Monoid.firstOptionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none()); + } + + /** get the first target of a {@link PTraversal} */ + public final Option headOption(final S s) { + return find(Function.constant(Boolean.TRUE)).f(s); + } + + /** check if at least one target satisfies the predicate */ + public final F exist(final F p) { + return foldMap(Monoid.disjunctionMonoid, p); + } + + /** check if all targets satisfy the predicate */ + public final F all(final F p) { + return foldMap(Monoid.conjunctionMonoid, p); + } + + /** modify polymorphically the target of a {@link PTraversal} with a function */ + public final F modify(final F f) { + return s -> this.modifyP1F(a -> P.p(f.f(a))).f(s)._1(); + } + + /** set polymorphically the target of a {@link PTraversal} with a value */ + public final F set(final B b) { + return modify(Function.constant(b)); + } + + /** join two {@link PTraversal} with the same target */ + public final PTraversal, Either, A, B> sum(final PTraversal other) { + final PTraversal self = this; + return new PTraversal, Either, A, B>() { + + @Override + public F, F>> modifyFunctionF(final F> f) { + return ss1 -> ss1.either( + s -> Function.compose(Either.left_(), self.modifyFunctionF(f).f(s)), + s1 -> Function.compose(Either.right_(), other.modifyFunctionF(f).f(s1)) + ); + } + + @Override + public F, Either>> modifyEitherF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyEitherF(f).f(s).right().map(Either.left_()), + s1 -> other.modifyEitherF(f).f(s1).right().map(Either.right_()) + ); + } + + @Override + public F, IO>> modifyIOF(final F> f) { + return ss1 -> ss1.either( + s -> IOFunctions.map(self.modifyIOF(f).f(s), Either.left_()), + s1 -> IOFunctions.map(other.modifyIOF(f).f(s1), Either.right_()) + ); + } + + @Override + public F, Trampoline>> modifyTrampolineF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyTrampolineF(f).f(s).map(Either.left_()), + s1 -> other.modifyTrampolineF(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, Promise>> modifyPromiseF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyPromiseF(f).f(s).fmap(Either.left_()), + s1 -> other.modifyPromiseF(f).f(s1).fmap(Either.right_()) + ); + } + + @Override + public F, List>> modifyListF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyListF(f).f(s).map(Either.left_()), + s1 -> other.modifyListF(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, Option>> modifyOptionF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyOptionF(f).f(s).map(Either.left_()), + s1 -> other.modifyOptionF(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, Stream>> modifyStreamF(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyStreamF(f).f(s).map(Either.left_()), + s1 -> other.modifyStreamF(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, P1>> modifyP1F(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyP1F(f).f(s).map(Either.left_()), + s1 -> other.modifyP1F(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, V2>> modifyV2F(final F> f) { + return ss1 -> ss1.either( + s -> self.modifyV2F(f).f(s).map(Either.left_()), + s1 -> other.modifyV2F(f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, Validation>> modifyValidationF(Semigroup se, final F> f) { + return ss1 -> ss1.either( + s -> self.modifyValidationF(se, f).f(s).map(Either.left_()), + s1 -> other.modifyValidationF(se, f).f(s1).map(Either.right_()) + ); + } + + @Override + public F, M> foldMap(final Monoid monoid, final F f) { + return ss1 -> ss1.either( + self.foldMap(monoid, f), + other.foldMap(monoid, f) + ); + } + + }; + } + + /****************************************************************/ + /** Compose methods between a {@link PTraversal} and another Optics */ + /****************************************************************/ + + /** compose a {@link PTraversal} with a {@link Fold} */ + public final Fold composeFold(final Fold other) { + return asFold().composeFold(other); + } + + // + /** compose a {@link PTraversal} with a {@link Getter} */ + public final Fold composeFold(final Getter other) { + return asFold().composeGetter(other); + } + + /** compose a {@link PTraversal} with a {@link PSetter} */ + public final PSetter composeSetter(final PSetter other) { + return asSetter().composeSetter(other); + } + + /** compose a {@link PTraversal} with a {@link PTraversal} */ + public final PTraversal composeTraversal(final PTraversal other) { + final PTraversal self = this; + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return self.modifyFunctionF(other.modifyFunctionF(f)); + } + + @Override + public F> modifyEitherF(final F> f) { + return self.modifyEitherF(other.modifyEitherF(f)); + } + + @Override + public F> modifyIOF(final F> f) { + return self.modifyIOF(other.modifyIOF(f)); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return self.modifyTrampolineF(other.modifyTrampolineF(f)); + } + + @Override + public F> modifyPromiseF(final F> f) { + return self.modifyPromiseF(other.modifyPromiseF(f)); + } + + @Override + public F> modifyListF(final F> f) { + return self.modifyListF(other.modifyListF(f)); + } + + @Override + public F> modifyOptionF(final F> f) { + return self.modifyOptionF(other.modifyOptionF(f)); + } + + @Override + public F> modifyStreamF(final F> f) { + return self.modifyStreamF(other.modifyStreamF(f)); + } + + @Override + public F> modifyP1F(final F> f) { + return self.modifyP1F(other.modifyP1F(f)); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return self.modifyValidationF(s, other.modifyValidationF(s, f)); + } + + @Override + public F> modifyV2F(final F> f) { + return self.modifyV2F(other.modifyV2F(f)); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return self.foldMap(monoid, other.foldMap(monoid, f)); + } + }; + } + + /** compose a {@link PTraversal} with a {@link POptional} */ + public final PTraversal composeOptional(final POptional other) { + return composeTraversal(other.asTraversal()); + } + + /** compose a {@link PTraversal} with a {@link PPrism} */ + public final PTraversal composePrism(final PPrism other) { + return composeTraversal(other.asTraversal()); + } + + /** compose a {@link PTraversal} with a {@link PLens} */ + public final PTraversal composeLens(final PLens other) { + return composeTraversal(other.asTraversal()); + } + + /** compose a {@link PTraversal} with a {@link PIso} */ + public final PTraversal composeIso(final PIso other) { + return composeTraversal(other.asTraversal()); + } + + /**********************************************************************/ + /** Transformation methods to view a {@link PTraversal} as another Optics */ + /**********************************************************************/ + + /** view a {@link PTraversal} as a {@link Fold} */ + public final Fold asFold() { + return new Fold() { + @Override + public F foldMap(final Monoid monoid, final F f) { + return PTraversal.this.foldMap(monoid, f); + } + }; + } + + /** view a {@link PTraversal} as a {@link PSetter} */ + public PSetter asSetter() { + return PSetter.pSetter(this::modify); + } + + public static PTraversal pId() { + return PIso. pId().asTraversal(); + } + + public static PTraversal, Either, S, T> pCodiagonal() { + return new PTraversal, Either, S, T>() { + + @Override + public F, F>> modifyFunctionF(final F> f) { + return s -> s.bimap(f, f).either( + f1 -> Function.compose(Either.left_(), f1), + f1 -> Function.compose(Either.right_(), f1) + ); + } + + @Override + public F, Either>> modifyEitherF(final F> f) { + return s -> s.bimap(f, f).either( + e -> e.right().map(Either.left_()), + e -> e.right().map(Either.right_()) + ); + } + + @Override + public F, IO>> modifyIOF(final F> f) { + return s -> s.bimap(f, f).either( + io -> IOFunctions.map(io, Either.left_()), + io -> IOFunctions.map(io, Either.right_()) + ); + } + + @Override + public F, Trampoline>> modifyTrampolineF(final F> f) { + return s -> s.bimap(f, f).either( + t -> t.map(Either.left_()), + t -> t.map(Either.right_()) + ); + } + + @Override + public F, Promise>> modifyPromiseF(final F> f) { + return s -> s.bimap(f, f).either( + p -> p.fmap(Either.left_()), + p -> p.fmap(Either.right_()) + ); + } + + @Override + public F, List>> modifyListF(final F> f) { + return s -> s.bimap(f, f).either( + l -> l.map(Either.left_()), + l -> l.map(Either.right_()) + ); + } + + @Override + public F, Option>> modifyOptionF(final F> f) { + return s -> s.bimap(f, f).either( + o -> o.map(Either.left_()), + o -> o.map(Either.right_()) + ); + } + + @Override + public F, Stream>> modifyStreamF(final F> f) { + return s -> s.bimap(f, f).either( + stream -> stream.map(Either.left_()), + stream -> stream.map(Either.right_()) + ); + } + + @Override + public F, P1>> modifyP1F(final F> f) { + return s -> s.bimap(f, f).either( + p1 -> p1.map(Either.left_()), + p1 -> p1.map(Either.right_()) + ); + } + + @Override + public F, V2>> modifyV2F(final F> f) { + return s -> s.bimap(f, f).either( + v2 -> v2.map(Either.left_()), + v2 -> v2.map(Either.right_()) + ); + } + + @Override + 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_()) + ); + } + + @Override + public F, M> foldMap(final Monoid monoid, final F f) { + return s -> s.either(f, f); + } + }; + } + + public static PTraversal pTraversal(final F get1, final F get2, + final F3 set) { + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return s -> Function.apply(Function.compose(b1 -> b2 -> set.f(b1, b2, s), f.f(get1.f(s))), f.f(get2.f(s))); + } + + @Override + public F> modifyEitherF(final F> f) { + return s -> f.f(get2.f(s)).right().apply(f.f(get1.f(s)).right().> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyIOF(final F> f) { + return s -> IOFunctions.apply(f.f(get2.f(s)), + IOFunctions.> map(f.f(get1.f(s)), b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyPromiseF(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> fmap(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyListF(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyOptionF(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyStreamF(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyP1F(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + public F> modifyV2F(final F> f) { + return s -> f.f(get2.f(s)).apply(f.f(get1.f(s)).> map(b1 -> b2 -> set.f(b1, b2, s))); + } + + @Override + 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 + public F foldMap(final Monoid monoid, final F f) { + return s -> monoid.sum(f.f(get1.f(s)), f.f(get2.f(s))); + } + }; + } + + 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); + } + + public static PTraversal pTraversal(final F get1, final F get2, final F get3, + final F get4, + final F5 set) { + return fromCurried(pTraversal(get1, get2, get3, (b1, b2, b3, s) -> b4 -> set.f(b1, b2, b3, b4, s)), get4); + } + + public static PTraversal pTraversal(final F get1, final F get2, final F get3, + final F get4, final F get5, + final F6 set) { + return fromCurried(pTraversal(get1, get2, get3, get4, (b1, b2, b3, b4, s) -> b5 -> set.f(b1, b2, b3, b4, b5, s)), get5); + } + + public static PTraversal pTraversal(final F get1, final F get2, final F get3, + final F get4, final F get5, final F get6, + final F7 set) { + return fromCurried( + pTraversal(get1, get2, get3, get4, get5, (b1, b2, b3, b4, b5, s) -> b6 -> set.f(b1, b2, b3, b4, b5, b6, s)), + get6); + } + + private static PTraversal fromCurried(final PTraversal, A, B> curriedTraversal, + final F lastGet) { + return new PTraversal() { + + @Override + public F> modifyFunctionF(final F> f) { + return s -> Function.apply(curriedTraversal.modifyFunctionF(f).f(s), f.f(lastGet.f(s))); + } + + @Override + public F> modifyEitherF(final F> f) { + return s -> f.f(lastGet.f(s)).right().apply(curriedTraversal.modifyEitherF(f).f(s)); + } + + @Override + public F> modifyIOF(final F> f) { + return s -> IOFunctions.apply(f.f(lastGet.f(s)), curriedTraversal.modifyIOF(f).f(s)); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyTrampolineF(f).f(s)); + } + + @Override + public F> modifyPromiseF(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyPromiseF(f).f(s)); + } + + @Override + public F> modifyListF(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyListF(f).f(s)); + } + + @Override + public F> modifyOptionF(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyOptionF(f).f(s)); + } + + @Override + public F> modifyStreamF(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyStreamF(f).f(s)); + } + + @Override + public F> modifyP1F(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyP1F(f).f(s)); + } + + @Override + public F> modifyV2F(final F> f) { + return s -> f.f(lastGet.f(s)).apply(curriedTraversal.modifyV2F(f).f(s)); + } + + @Override + public F> modifyValidationF(Semigroup se, final F> f) { + return s -> f.f(lastGet.f(s)).accumapply(se, curriedTraversal.modifyValidationF(se, f).f(s)); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return s -> monoid.sum(curriedTraversal.foldMap(monoid, f).f(s), f.f(lastGet.f(s))); + } + }; + } +} diff --git a/core/src/main/java/fj/data/optic/Prism.java b/core/src/main/java/fj/data/optic/Prism.java new file mode 100644 index 00000000..c2160b1f --- /dev/null +++ b/core/src/main/java/fj/data/optic/Prism.java @@ -0,0 +1,113 @@ +package fj.data.optic; + +import fj.F; +import fj.data.Either; +import fj.data.Option; + +/** + * {@link PPrism} restricted to monomorphic update + */ +public final class Prism extends PPrism { + + final PPrism pPrism; + + public Prism(final PPrism pPrism) { + this.pPrism = pPrism; + } + + @Override + public Either getOrModify(final S s) { + return pPrism.getOrModify(s); + } + + @Override + public S reverseGet(final A a) { + return pPrism.reverseGet(a); + } + + @Override + public Option getOption(final S s) { + return pPrism.getOption(s); + } + + /***********************************************************/ + /** Compose methods between a {@link Prism} and another Optics */ + /***********************************************************/ + + /** compose a {@link Prism} with a {@link Setter} */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pPrism.composeSetter(other.pSetter)); + } + + /** compose a {@link Prism} with a {@link Traversal} */ + public Traversal composeTraversal(final Traversal other) { + return new Traversal<>(pPrism.composeTraversal(other.pTraversal)); + } + + /** compose a {@link Prism} with a {@link Optional} */ + public Optional composeOptional(final Optional other) { + return new Optional<>(pPrism.composeOptional(other.pOptional)); + } + + /** compose a {@link Prism} with a {@link Lens} */ + public Optional composeLens(final Lens other) { + return new Optional<>(pPrism.composeLens(other.pLens)); + } + + /** compose a {@link Prism} with a {@link Prism} */ + public Prism composePrism(final Prism other) { + return new Prism<>(pPrism.composePrism(other.pPrism)); + } + + /** compose a {@link Prism} with an {@link Iso} */ + public Prism composeIso(final Iso other) { + return new Prism<>(pPrism.composeIso(other.pIso)); + } + + /*****************************************************************/ + /** Transformation methods to view a {@link Prism} as another Optics */ + /*****************************************************************/ + + /** view a {@link Prism} as a {@link Setter} */ + @Override + public Setter asSetter() { + return new Setter<>(pPrism.asSetter()); + } + + /** view a {@link Prism} as a {@link Traversal} */ + @Override + public Traversal asTraversal() { + return new Traversal<>(pPrism.asTraversal()); + } + + /** view a {@link Prism} as a {@link Optional} */ + @Override + public Optional asOptional() { + return new Optional<>(pPrism.asOptional()); + } + + public static Prism id() { + return new Prism<>(PPrism.pId()); + } + + public static Prism prism(final F> getOption, final F reverseGet) { + return new Prism<>(new PPrism() { + + @Override + public Either getOrModify(final S s) { + return getOption.f(s).option(Either.left(s), Either.right_()); + } + + @Override + public S reverseGet(final A a) { + return reverseGet.f(a); + } + + @Override + public Option getOption(final S s) { + return getOption.f(s); + } + }); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fj/data/optic/Setter.java b/core/src/main/java/fj/data/optic/Setter.java new file mode 100644 index 00000000..95227f10 --- /dev/null +++ b/core/src/main/java/fj/data/optic/Setter.java @@ -0,0 +1,61 @@ +package fj.data.optic; + +import fj.F; +import fj.data.Either; + +/** {@link PSetter} with a monomorphic modify function */ +public final class Setter extends PSetter { + + final PSetter pSetter; + + public Setter(final PSetter pSetter) { + this.pSetter = pSetter; + } + + @Override + public F modify(final F f) { + return pSetter.modify(f); + } + + @Override + public F set(final A b) { + return pSetter.set(b); + } + + /** join two {@link Setter} with the same target */ + public Setter, A> sum(final Setter other) { + return new Setter<>(pSetter.sum(other.pSetter)); + } + + /************************************************************/ + /** Compose methods between a {@link Setter} and another Optics */ + /************************************************************/ + + /** compose a {@link Setter} with a {@link Setter} */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pSetter.composeSetter(other.pSetter)); + } + + /** compose a {@link Setter} with a {@link Traversal} */ + public Setter composeTraversal(final Traversal other) { + return new Setter<>(pSetter.composeTraversal(other.pTraversal)); + } + + /** compose a {@link Setter} with an {@link Iso} */ + public Setter composeIso(final Iso other) { + return new Setter<>(pSetter.composeIso(other.pIso)); + } + + public static Setter id() { + return new Setter<>(PSetter.pId()); + } + + public static Setter, S> codiagonal() { + return new Setter<>(PSetter.pCodiagonal()); + } + + /** alias for {@link PSetter} constructor with a monomorphic modify function */ + 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 new file mode 100644 index 00000000..a1a466d8 --- /dev/null +++ b/core/src/main/java/fj/data/optic/Traversal.java @@ -0,0 +1,152 @@ +package fj.data.optic; + +import fj.F; +import fj.F3; +import fj.F4; +import fj.F5; +import fj.F6; +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; +import fj.data.IO; +import fj.data.List; +import fj.data.Option; +import fj.data.Stream; +import fj.data.Validation; +import fj.data.vector.V2; + +public final class Traversal extends PTraversal { + + final PTraversal pTraversal; + + public Traversal(final PTraversal pTraversal) { + this.pTraversal = pTraversal; + } + + @Override + public F> modifyFunctionF(final F> f) { + return pTraversal.modifyFunctionF(f); + } + + @Override + public F> modifyEitherF(final F> f) { + return pTraversal.modifyEitherF(f); + } + + @Override + public F> modifyIOF(final F> f) { + return pTraversal.modifyIOF(f); + } + + @Override + public F> modifyTrampolineF(final F> f) { + return pTraversal.modifyTrampolineF(f); + } + + @Override + public F> modifyPromiseF(final F> f) { + return pTraversal.modifyPromiseF(f); + } + + @Override + public F> modifyListF(final F> f) { + return pTraversal.modifyListF(f); + } + + @Override + public F> modifyOptionF(final F> f) { + return pTraversal.modifyOptionF(f); + } + + @Override + public F> modifyStreamF(final F> f) { + return pTraversal.modifyStreamF(f); + } + + @Override + public F> modifyP1F(final F> f) { + return pTraversal.modifyP1F(f); + } + + @Override + public F> modifyValidationF(Semigroup s, final F> f) { + return pTraversal.modifyValidationF(s, f); + } + + @Override + public F> modifyV2F(final F> f) { + return pTraversal.modifyV2F(f); + } + + @Override + public F foldMap(final Monoid monoid, final F f) { + return pTraversal.foldMap(monoid, f); + } + + /** join two {@link Traversal} with the same target */ + public Traversal, A> sum(final Traversal other) { + return new Traversal<>(pTraversal.sum(other.pTraversal)); + } + + /***************************************************************/ + /** Compose methods between a {@link Traversal} and another Optics */ + /***************************************************************/ + + /** compose a {@link Traversal} with a {@link Setter} */ + public Setter composeSetter(final Setter other) { + return new Setter<>(pTraversal.composeSetter(other.pSetter)); + } + + /** compose a {@link Traversal} with a {@link Traversal} */ + public Traversal composeTraversal(final Traversal other) { + return new Traversal<>(pTraversal.composeTraversal(other.pTraversal)); + } + + /*********************************************************************/ + /** Transformation methods to view a {@link Traversal} as another Optics */ + /*********************************************************************/ + + /** view a {@link Traversal} as a {@link Setter} */ + @Override + public Setter asSetter() { + return new Setter<>(pTraversal.asSetter()); + } + + public static Traversal id() { + return new Traversal<>(PTraversal.pId()); + } + + public static Traversal, S> codiagonal() { + return new Traversal<>(PTraversal.pCodiagonal()); + } + + public static Traversal traversal(final F get1, final F get2, final F3 set) { + return new Traversal<>(PTraversal.pTraversal(get1, get2, set)); + } + + public static Traversal traversal(final F get1, final F get2, final F get3, + final F4 set) { + return new Traversal<>(PTraversal.pTraversal(get1, get2, get3, set)); + } + + public static Traversal traversal(final F get1, final F get2, final F get3, + final F get4, final F5 set) { + return new Traversal<>(PTraversal.pTraversal(get1, get2, get3, get4, set)); + } + + public static Traversal traversal(final F get1, final F get2, final F get3, + final F get4, final F get5, final F6 set) { + return new Traversal<>(PTraversal.pTraversal(get1, get2, get3, get4, get5, set)); + } + + public static Traversal traversal(final F get1, final F get2, final F get3, + final F get4, final F get5, final F get6, final F7 set) { + return new Traversal<>(PTraversal.pTraversal(get1, get2, get3, get4, get5, get6, + set)); + } + +} diff --git a/core/src/main/java/fj/data/optic/package-info.java b/core/src/main/java/fj/data/optic/package-info.java new file mode 100644 index 00000000..d12f58ae --- /dev/null +++ b/core/src/main/java/fj/data/optic/package-info.java @@ -0,0 +1,11 @@ +/** + * + * Optic data types adapted from the Scala Monocle library + * and inspired by the + * Haskell Lens library. See the Monocle Github + * page for an overview of the package. + * + * @version %build.number% + * @see Monocle + */ +package fj.data.optic; 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 4f551be2..70d60f58 100644 --- a/core/src/main/java/fj/data/vector/V.java +++ b/core/src/main/java/fj/data/vector/V.java @@ -1,12 +1,12 @@ package fj.data.vector; +import fj.F0; import fj.F2; import fj.F3; import fj.F4; import fj.F5; import fj.P; import fj.P1; -import fj.P2; /** * Functions across vectors. @@ -34,16 +34,8 @@ public static V2 v(final A a1, final A a2) { * @param a2 An element to put in a vector. * @return The vector-2. */ - public static V2 v(final P1 a1, final P1 a2) { - return V2.p(new P2() { - public A _1() { - return a1._1(); - } - - public A _2() { - return a2._1(); - } - }); + public static V2 v(final F0 a1, final F0 a2) { + return V2.p(P.lazy(a1, a2)); } /** @@ -52,11 +44,7 @@ public A _2() { * @return A function that puts elements in a vector-2. */ public static F2> v2() { - return new F2>() { - public V2 f(final A a, final A a1) { - return v(a, a1); - } - }; + return V::v; } /** @@ -79,7 +67,7 @@ public static V3 v(final A a1, final A a2, final A a3) { * @param a3 An element to put in a vector. * @return The vector-3. */ - public static V3 v(final P1 a1, final P1 a2, final P1 a3) { + public static V3 v(final P1 a1, final F0 a2, final F0 a3) { return V3.cons(a1, v(a2, a3)); } @@ -89,11 +77,7 @@ public static V3 v(final P1 a1, final P1 a2, final P1 a3) { * @return A function that puts elements in a vector-3. */ public static F3> v3() { - return new F3>() { - public V3 f(final A a, final A a1, final A a2) { - return v(a, a1, a2); - } - }; + return V::v; } /** @@ -118,7 +102,7 @@ public static V4 v(final A a1, final A a2, final A a3, final A a4) { * @param a4 An element to put in a vector. * @return The vector-4. */ - public static V4 v(final P1 a1, final P1 a2, final P1 a3, final P1 a4) { + public static V4 v(final P1 a1, final P1 a2, final F0 a3, final F0 a4) { return V4.cons(a1, v(a2, a3, a4)); } @@ -128,11 +112,7 @@ public static V4 v(final P1 a1, final P1 a2, final P1 a3, final * @return A function that puts elements in a vector-4. */ public static F4> v4() { - return new F4>() { - public V4 f(final A a, final A a1, final A a2, final A a3) { - return v(a, a1, a2, a3); - } - }; + return V::v; } @@ -160,7 +140,7 @@ public static V5 v(final A a1, final A a2, final A a3, final A a4, final * @param a5 An element to put in a vector. * @return The vector-5. */ - public static V5 v(final P1 a1, final P1 a2, final P1 a3, final P1 a4, final P1 a5) { + public static V5 v(final P1 a1, final P1 a2, final P1 a3, final F0 a4, final F0 a5) { return V5.cons(a1, v(a2, a3, a4, a5)); } @@ -170,11 +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 new F5>() { - public V5 f(final A a, final A a1, final A a2, final A a3, final A a4) { - return 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 8cd6ca81..d1ec5e74 100644 --- a/core/src/main/java/fj/data/vector/V2.java +++ b/core/src/main/java/fj/data/vector/V2.java @@ -1,11 +1,10 @@ package fj.data.vector; -import fj.F; -import fj.F2; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; -import fj.P1; -import fj.P2; + import fj.data.Array; import fj.data.List; import fj.data.NonEmptyList; @@ -24,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. * @@ -31,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); } /** @@ -58,11 +67,7 @@ public A _2() { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V2 v) { - return v._1(); - } - }; + return V2::_1; } /** @@ -71,11 +76,7 @@ public A f(final V2 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V2 v) { - return v._2(); - } - }; + return V2::_2; } /** @@ -111,11 +112,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(_1(), new P1>() { - public Stream _1() { - return Stream.single(_2()); - } - }); + return Stream.cons(_1(), () -> Stream.single(_2())); } /** @@ -124,11 +121,7 @@ public Stream _1() { * @return a function that transforms a vector-2 to a stream of its elements. */ public static F, Stream> toStream_() { - return new F, Stream>() { - public Stream f(final V2 v) { - return v.toStream(); - } - }; + return V2::toStream; } /** @@ -137,11 +130,7 @@ public Stream f(final V2 v) { * @return a function that transforms a vector-2 to the equivalent product-2. */ public static F, P2> p_() { - return new F, P2>() { - public P2 f(final V2 v) { - return v.p(); - } - }; + return V2::p; } /** @@ -214,11 +203,7 @@ public V2> vzip(final V2 bs) { * @return the first element of this vector as a product-1. */ public P1 head() { - return new P1() { - public A _1() { - return 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 5d7dcfed..523c08ae 100644 --- a/core/src/main/java/fj/data/vector/V3.java +++ b/core/src/main/java/fj/data/vector/V3.java @@ -1,10 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P3; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -26,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. * @@ -33,19 +40,10 @@ private V3(final P1 head, final V2 tail) { * @return A new vector-3. */ public static V3 p(final P3 p) { - return new V3(new P1() { - public A _1() { - return p._1(); - } - }, V2.p(new P2() { - public A _1() { - return p._2(); - } - - public A _2() { - return p._3(); - } - })); + return new V3<>( + P.lazy(p::_1), + V2.p(P.lazy(p::_2, p::_3)) + ); } /** @@ -56,7 +54,7 @@ public A _2() { * @return The new vector. */ public static V3 cons(final P1 head, final V2 tail) { - return new V3(head, tail); + return new V3<>(head, tail); } /** @@ -143,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())); } /** @@ -204,11 +202,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head()._1(), new P1>() { - public Stream _1() { - return tail().toStream(); - } - }); + return Stream.cons(head()._1(), () -> tail().toStream()); } /** @@ -218,7 +212,7 @@ public Stream _1() { * @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)); } /** @@ -227,11 +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 new F, Stream>() { - public Stream f(final V3 v) { - return v.toStream(); - } - }; + return V3::toStream; } /** @@ -240,11 +230,7 @@ public Stream f(final V3 v) { * @return a function that transforms a vector-3 to the equivalent product-3. */ public static F, P3> p_() { - return new F, P3>() { - public P3 f(final V3 v) { - return v.p(); - } - }; + return V3::p; } /** @@ -253,11 +239,7 @@ public P3 f(final V3 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V3 v) { - return v._1(); - } - }; + return V3::_1; } /** @@ -266,11 +248,7 @@ public A f(final V3 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V3 v) { - return v._2(); - } - }; + return V3::_2; } /** @@ -279,11 +257,7 @@ public A f(final V3 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V3 v) { - return 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 960c52e9..808e7bab 100644 --- a/core/src/main/java/fj/data/vector/V4.java +++ b/core/src/main/java/fj/data/vector/V4.java @@ -1,11 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P3; -import fj.P4; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -27,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. * @@ -34,23 +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(new P1() { - public A _1() { - return 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(); + } + })); } /** @@ -61,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); } /** @@ -167,11 +170,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), new P1>() { - public Stream _1() { - return tail.toStream(); - } - }); + return Stream.cons(head._1(), tail::toStream); } /** @@ -191,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)); } /** @@ -201,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())); } /** @@ -244,11 +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 new F, Stream>() { - public Stream f(final V4 v) { - return v.toStream(); - } - }; + return V4::toStream; } /** @@ -257,11 +252,7 @@ public Stream f(final V4 v) { * @return a function that transforms a vector-4 to the equivalent product-4. */ public static F, P4> p_() { - return new F, P4>() { - public P4 f(final V4 v) { - return v.p(); - } - }; + return V4::p; } /** @@ -270,11 +261,7 @@ public P4 f(final V4 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V4 v) { - return v._1(); - } - }; + return V4::_1; } /** @@ -283,11 +270,7 @@ public A f(final V4 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V4 v) { - return v._2(); - } - }; + return V4::_2; } /** @@ -296,11 +279,7 @@ public A f(final V4 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V4 v) { - return v._3(); - } - }; + return V4::_3; } /** @@ -309,10 +288,6 @@ public A f(final V4 v) { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return new F, A>() { - public A f(final V4 v) { - return 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 22564862..8f0583a3 100644 --- a/core/src/main/java/fj/data/vector/V5.java +++ b/core/src/main/java/fj/data/vector/V5.java @@ -1,11 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P4; -import fj.P5; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -27,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. * @@ -34,27 +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(new P1() { - public A _1() { - return 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(); + } + })); } /** @@ -65,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); } /** @@ -184,11 +187,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), new P1>() { - public Stream _1() { - return tail.toStream(); - } - }); + return Stream.cons(head._1(), tail::toStream); } /** @@ -208,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)); } /** @@ -218,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())); } /** @@ -261,11 +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 new F, Stream>() { - public Stream f(final V5 v) { - return v.toStream(); - } - }; + return V5::toStream; } /** @@ -274,11 +269,7 @@ public Stream f(final V5 v) { * @return a function that transforms a vector-5 to the equivalent product-5. */ public static F, P5> p_() { - return new F, P5>() { - public P5 f(final V5 v) { - return v.p(); - } - }; + return V5::p; } /** @@ -287,11 +278,7 @@ public P5 f(final V5 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V5 v) { - return v._1(); - } - }; + return V5::_1; } /** @@ -300,11 +287,7 @@ public A f(final V5 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V5 v) { - return v._2(); - } - }; + return V5::_2; } /** @@ -313,11 +296,7 @@ public A f(final V5 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V5 v) { - return v._3(); - } - }; + return V5::_3; } /** @@ -326,11 +305,7 @@ public A f(final V5 v) { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return new F, A>() { - public A f(final V5 v) { - return v._4(); - } - }; + return V5::_4; } /** @@ -339,10 +314,6 @@ public A f(final V5 v) { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return new F, A>() { - public A f(final V5 v) { - return 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 031b5761..880deaff 100644 --- a/core/src/main/java/fj/data/vector/V6.java +++ b/core/src/main/java/fj/data/vector/V6.java @@ -1,11 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P5; -import fj.P6; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -27,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. * @@ -34,31 +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(new P1() { - public A _1() { - return 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(); + } + })); } /** @@ -69,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); } /** @@ -201,11 +204,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), new P1>() { - public Stream _1() { - return tail.toStream(); - } - }); + return Stream.cons(head._1(), tail::toStream); } /** @@ -225,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)); } /** @@ -235,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())); } /** @@ -278,11 +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 new F, Stream>() { - public Stream f(final V6 v) { - return v.toStream(); - } - }; + return V6::toStream; } /** @@ -291,11 +286,7 @@ public Stream f(final V6 v) { * @return a function that transforms a vector-6 to the equivalent product-6. */ public static F, P6> p_() { - return new F, P6>() { - public P6 f(final V6 v) { - return v.p(); - } - }; + return V6::p; } /** @@ -304,11 +295,7 @@ public P6 f(final V6 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V6 v) { - return v._1(); - } - }; + return V6::_1; } /** @@ -317,11 +304,7 @@ public A f(final V6 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V6 v) { - return v._2(); - } - }; + return V6::_2; } /** @@ -330,11 +313,7 @@ public A f(final V6 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V6 v) { - return v._3(); - } - }; + return V6::_3; } /** @@ -343,11 +322,7 @@ public A f(final V6 v) { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return new F, A>() { - public A f(final V6 v) { - return v._4(); - } - }; + return V6::_4; } /** @@ -356,11 +331,7 @@ public A f(final V6 v) { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return new F, A>() { - public A f(final V6 v) { - return v._5(); - } - }; + return V6::_5; } /** @@ -369,11 +340,7 @@ public A f(final V6 v) { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return new F, A>() { - public A f(final V6 v) { - return 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 8db9db6d..2127d5f4 100644 --- a/core/src/main/java/fj/data/vector/V7.java +++ b/core/src/main/java/fj/data/vector/V7.java @@ -1,11 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P6; -import fj.P7; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -27,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. * @@ -34,35 +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(new P1() { - public A _1() { - return 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(); + } + })); } /** @@ -73,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); } /** @@ -218,11 +221,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), new P1>() { - public Stream _1() { - return tail.toStream(); - } - }); + return Stream.cons(head._1(), tail::toStream); } /** @@ -242,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)); } /** @@ -252,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())); } /** @@ -295,11 +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 new F, Stream>() { - public Stream f(final V7 v) { - return v.toStream(); - } - }; + return V7::toStream; } /** @@ -308,11 +303,7 @@ public Stream f(final V7 v) { * @return a function that transforms a vector-7 to the equivalent product-7. */ public static F, P7> p_() { - return new F, P7>() { - public P7 f(final V7 v) { - return v.p(); - } - }; + return V7::p; } /** @@ -321,11 +312,7 @@ public P7 f(final V7 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V7 v) { - return v._1(); - } - }; + return V7::_1; } /** @@ -334,11 +321,7 @@ public A f(final V7 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V7 v) { - return v._2(); - } - }; + return V7::_2; } /** @@ -347,11 +330,7 @@ public A f(final V7 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V7 v) { - return v._3(); - } - }; + return V7::_3; } /** @@ -360,11 +339,7 @@ public A f(final V7 v) { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return new F, A>() { - public A f(final V7 v) { - return v._4(); - } - }; + return V7::_4; } /** @@ -373,11 +348,7 @@ public A f(final V7 v) { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return new F, A>() { - public A f(final V7 v) { - return v._5(); - } - }; + return V7::_5; } /** @@ -386,11 +357,7 @@ public A f(final V7 v) { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return new F, A>() { - public A f(final V7 v) { - return v._6(); - } - }; + return V7::_6; } /** @@ -399,11 +366,7 @@ public A f(final V7 v) { * @return a function that gets the seventh element of a given vector. */ public static F, A> __7() { - return new F, A>() { - public A f(final V7 v) { - return 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 b29635d0..9ca7b618 100644 --- a/core/src/main/java/fj/data/vector/V8.java +++ b/core/src/main/java/fj/data/vector/V8.java @@ -1,11 +1,7 @@ package fj.data.vector; -import fj.F; -import fj.F2; -import fj.P1; -import fj.P2; -import fj.P7; -import fj.P8; +import fj.*; + import static fj.Function.curry; import static fj.P.p2; import fj.data.Array; @@ -27,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. * @@ -34,39 +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(new P1() { - public A _1() { - return 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(); + } + })); } /** @@ -77,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); } /** @@ -235,11 +238,7 @@ public NonEmptyList toNonEmptyList() { * @return a stream of the elements of this vector. */ public Stream toStream() { - return Stream.cons(head._1(), new P1>() { - public Stream _1() { - return tail.toStream(); - } - }); + return Stream.cons(head._1(), tail::toStream); } /** @@ -259,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)); } /** @@ -269,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())); } /** @@ -312,11 +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 new F, Stream>() { - public Stream f(final V8 v) { - return v.toStream(); - } - }; + return V8::toStream; } /** @@ -325,11 +320,7 @@ public Stream f(final V8 v) { * @return a function that transforms a vector-8 to the equivalent product-8. */ public static F, P8> p_() { - return new F, P8>() { - public P8 f(final V8 v) { - return v.p(); - } - }; + return V8::p; } @@ -339,11 +330,7 @@ public P8 f(final V8 v) { * @return a function that gets the first element of a given vector. */ public static F, A> __1() { - return new F, A>() { - public A f(final V8 v) { - return v._1(); - } - }; + return V8::_1; } /** @@ -352,11 +339,7 @@ public A f(final V8 v) { * @return a function that gets the second element of a given vector. */ public static F, A> __2() { - return new F, A>() { - public A f(final V8 v) { - return v._2(); - } - }; + return V8::_2; } /** @@ -365,11 +348,7 @@ public A f(final V8 v) { * @return a function that gets the third element of a given vector. */ public static F, A> __3() { - return new F, A>() { - public A f(final V8 v) { - return v._3(); - } - }; + return V8::_3; } /** @@ -378,11 +357,7 @@ public A f(final V8 v) { * @return a function that gets the fourth element of a given vector. */ public static F, A> __4() { - return new F, A>() { - public A f(final V8 v) { - return v._4(); - } - }; + return V8::_4; } /** @@ -391,11 +366,7 @@ public A f(final V8 v) { * @return a function that gets the fifth element of a given vector. */ public static F, A> __5() { - return new F, A>() { - public A f(final V8 v) { - return v._5(); - } - }; + return V8::_5; } /** @@ -404,11 +375,7 @@ public A f(final V8 v) { * @return a function that gets the sixth element of a given vector. */ public static F, A> __6() { - return new F, A>() { - public A f(final V8 v) { - return v._6(); - } - }; + return V8::_6; } /** @@ -417,11 +384,7 @@ public A f(final V8 v) { * @return a function that gets the seventh element of a given vector. */ public static F, A> __7() { - return new F, A>() { - public A f(final V8 v) { - return v._7(); - } - }; + return V8::_7; } /** @@ -430,11 +393,7 @@ public A f(final V8 v) { * @return a function that gets the eighth element of a given vector. */ public static F, A> __8() { - return new F, A>() { - public A f(final V8 v) { - return 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 2fd04a76..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,68 +20,40 @@ private BigIntegers() { * Curried Integer addition. */ public static final F> add = - curry(new F2() { - public BigInteger f(final BigInteger a1, final BigInteger a2) { - return a1.add(a2); - } - }); + curry((F2) BigInteger::add); /** * Curried Integer multiplication. */ public static final F> multiply = - curry(new F2() { - public BigInteger f(final BigInteger a1, final BigInteger a2) { - return a1.multiply(a2); - } - }); + curry(BigInteger::multiply); /** * Curried Integer subtraction. */ public static final F> subtract = - curry(new F2() { - public BigInteger f(final BigInteger a1, final BigInteger a2) { - return a1.subtract(a2); - } - }); + curry((F2) BigInteger::subtract); /** * Negation. */ - public static final F negate = new F() { - public BigInteger f(final BigInteger i) { - return i.negate(); - } - }; + public static final F negate = BigInteger::negate; /** * Absolute value. */ - public static final F abs = new F() { - public BigInteger f(final BigInteger i) { - return i.abs(); - } - }; + public static final F abs = BigInteger::abs; /** * Remainder. */ public static final F> remainder = - curry(new F2() { - public BigInteger f(final BigInteger a1, final BigInteger a2) { - return a1.remainder(a2); - } - }); + curry(BigInteger::remainder); /** * Power. */ - public static final F> power = curry(new F2() { - public BigInteger f(final BigInteger a1, final Integer a2) { - return 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 8b509cad..bef1b347 100644 --- a/core/src/main/java/fj/function/Booleans.java +++ b/core/src/main/java/fj/function/Booleans.java @@ -1,11 +1,11 @@ package fj.function; -import fj.F; -import fj.F2; -import fj.F3; + import static fj.Function.*; +import fj.F; import fj.Monoid; +import fj.Semigroup; import fj.data.List; import fj.data.Stream; @@ -15,8 +15,6 @@ /** * Curried logical functions. - * - * @version %build.number% */ public final class Booleans { private Booleans() { @@ -42,20 +40,12 @@ private Booleans() { /** * Logical negation. */ - public static final F not = new F() { - public Boolean f(final Boolean p) { - return !p; - } - }; + public static final F not = p -> !p; /** * Curried form of logical "only if" (material implication). */ - public static final F> implies = curry(new F2() { - public Boolean f(final Boolean p, final Boolean q) { - return !p || q; - } - }); + public static final F> implies = curry((p, q) -> !p || q); /** * Curried form of logical "if" (reverse material implication). @@ -92,6 +82,66 @@ public static boolean and(final List l) { return Monoid.conjunctionMonoid.sumLeft(l); } + /** + * maps given function to the predicate function + * @param p predicate to be mapped over + * @param f function + * @return predicate function + */ + public static F contramap(F f, F p){ + return compose(p, f); + } + + /** + * alias for contramap + * @param p predicate to be mapped over + * @param f function + * @return predicate function + */ + public static F is(F f, F p){ + return contramap(f, p); + } + + /** + * returns inverse of contramap + * @param p predicate to be mapped over + * @param f function + * @return predicate function + */ + public static F isnot(F f, F p){ + return compose(not, contramap(f, p)); + } + + /** + * composes the given predicate using conjunction + * @param p1 first predicate + * @param p2 second predicate + * @return composed predicate function + */ + public static F and(F p1, F p2){ + return Semigroup.functionSemigroup(conjunctionSemigroup).sum(p1, p2); + } + + /** + * composes the given predicate using exclusive disjunction + * @param p1 first predicate + * @param p2 second predicate + * @return composed predicate function + */ + public static F xor(F p1, F p2){ + return Semigroup.functionSemigroup(exclusiveDisjunctionSemiGroup).sum(p1, p2); + } + + /** + * returns composed predicate using disjunction + * @param p1 first predicate + * @param p2 second predicate + * @return composed predicate + */ + public static F or(F p1, F p2){ + return Semigroup.functionSemigroup(disjunctionSemigroup).sum(p1, p2); + } + /** * Returns true if all the elements of the given stream are true. * @@ -102,6 +152,46 @@ public static boolean and(final Stream l) { return Monoid.conjunctionMonoid.sumLeft(l); } + /** + * Returns composed predicate + * + * @param l A stream of predicates + * @return composed predicate + */ + public static F andAll(final Stream> l) { + return Monoid.functionMonoid(Monoid.conjunctionMonoid).sumLeft(l); + } + + /** + * Returns a composed predicate of given List of predicates + * + * @param l A list of predicate functions + * @return composed predicate function + */ + public static F andAll(final List> l) { + return Monoid.functionMonoid(Monoid.conjunctionMonoid).sumLeft(l); + } + + /** + * Returns a composed predicate of given List of predicates + * + * @param l A list of predicate functions + * @return composed predicate function + */ + public static F orAll(final List> l) { + return Monoid.functionMonoid(Monoid.disjunctionMonoid).sumLeft(l); + } + + /** + * Returns a composed predicate of given Stream of predicates + * + * @param l A stream of predicate functions + * @return composed predicate function + */ + public static F orAll(final Stream> l) { + return Monoid.functionMonoid(Monoid.disjunctionMonoid).sumLeft(l); + } + /** * Returns true if any element of the given list is true. * @@ -139,10 +229,6 @@ public static F not(final F p) { * @return A function that returns its second argument if the first argument is true, otherwise the third argument. */ public static F>> cond() { - return curry(new F3() { - public A f(final Boolean p, final A a1, final A a2) { - return p ? a1 : a2; - } - }); + return curry((p, a1, a2) -> p ? a1 : a2); } } diff --git a/core/src/main/java/fj/function/Characters.java b/core/src/main/java/fj/function/Characters.java index 3c5a57af..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; @@ -12,82 +11,31 @@ public final class Characters { private Characters() { throw new UnsupportedOperationException(); } - public static final F toString = new F() { - public String f(final Character c) {return Character.toString(c);} - }; - public static final F isLowerCase = new F() { - public Boolean f(final Character ch) {return Character.isLowerCase(ch);} - }; - public static final F isUpperCase = new F() { - public Boolean f(final Character ch) {return Character.isUpperCase(ch);} - }; - public static final F isTitleCase = new F() { - public Boolean f(final Character ch) {return Character.isTitleCase(ch);} - }; - public static final F isDigit = new F() { - public Boolean f(final Character ch) {return Character.isDigit(ch);} - }; - public static final F isDefined = new F() { - public Boolean f(final Character ch) {return Character.isDefined(ch);} - }; - public static final F isLetter = new F() { - public Boolean f(final Character ch) {return Character.isLetter(ch);} - }; - public static final F isLetterOrDigit = new F() { - public Boolean f(final Character ch) {return Character.isLetterOrDigit(ch);} - }; - public static final F isJavaIdentifierStart = new F() { - public Boolean f(final Character ch) {return Character.isJavaIdentifierStart(ch);} - }; - public static final F isJavaIdentifierPart = new F() { - public Boolean f(final Character ch) {return Character.isJavaIdentifierPart(ch);} - }; - public static final F isUnicodeIdentifierStart = new F() { - public Boolean f(final Character ch) {return Character.isUnicodeIdentifierStart(ch);} - }; - public static final F isUnicodeIdentifierPart = new F() { - public Boolean f(final Character ch) {return Character.isUnicodeIdentifierPart(ch);} - }; - public static final F isIdentifierIgnorable = new F() { - public Boolean f(final Character ch) {return Character.isIdentifierIgnorable(ch);} - }; - public static final F toLowerCase = new F() { - public Character f(final Character ch) {return Character.toLowerCase(ch);} - }; - public static final F toUpperCase = new F() { - public Character f(final Character ch) {return Character.toUpperCase(ch);} - }; - public static final F toTitleCase = new F() { - public Character f(final Character ch) {return Character.toTitleCase(ch);} - }; - public static final F> digit = curry(new F2() { - public Integer f(final Character ch, final Integer radix) {return Character.digit(ch, radix);} - }); - public static final F getNumericValue = new F() { - public Integer f(final Character ch) {return Character.getNumericValue(ch);} - }; - public static final F isSpaceChar = new F() { - public Boolean f(final Character ch) {return Character.isSpaceChar(ch);} - }; - public static final F isWhitespace = new F() { - public Boolean f(final Character ch) {return Character.isWhitespace(ch);} - }; - public static final F isISOControl = new F() { - public Boolean f(final Character ch) {return Character.isISOControl(ch);} - }; - public static final F getType = new F() { - public Integer f(final Character ch) {return Character.getType(ch);} - }; - public static final F getDirectionality = new F() { - public Byte f(final Character ch) {return Character.getDirectionality(ch);} - }; - public static final F isMirrored = new F() { - public Boolean f(final Character ch) {return Character.isMirrored(ch);} - }; - public static final F reverseBytes = new F() { - public Character f(final Character ch) {return Character.reverseBytes(ch);} - }; - public static final F isNewLine = new F() { - public Boolean f(final Character c) { return c == '\n'; } - }; + public static final F toString = c -> Character.toString(c); + 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 = 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 51f3981b..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,66 +20,42 @@ 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(new F2() { - public Double f(final Double x, final Double y) { - return x - y; - } - }); + public static final F> subtract = x -> y -> x - y; /** * Negation. */ - public static final F negate = new F() { - public Double f(final Double x) { - return x * -1; - } - }; + public static final F negate = x -> x * -1; /** * Absolute value. */ - public static final F abs = new F() { - public Double f(final Double x) { - return abs(x); - } - }; + public static final F abs = Math::abs; /** * Remainder. */ - public static final F> remainder = curry(new F2() { - public Double f(final Double a, final Double b) { - return a % b; - } - }); + public static final F> remainder = x -> y -> x % y; /** * Power. */ - public static final F> power = curry(new F2() { - public Double f(final Double a, final Double b) { - return StrictMath.pow(a, b); - } - }); + public static final F> power = x -> y -> StrictMath.pow(x, y); /** * Evenness. */ - public static final F even = new F() { - public Boolean f(final Double i) { - return i % 2 == 0; - } - }; + public static final F even = i -> i % 2 == 0; /** * Sums a list of doubles. @@ -94,7 +64,7 @@ public Boolean f(final Double i) { * @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); } /** @@ -104,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); } /** @@ -113,49 +83,32 @@ public static double product(final List doubles) { * @return A function that converts strings to doubles. */ public static F> fromString() { - return new F>() { - public Option f(final String s) { + return s -> { try { return some(Double.valueOf(s)); } catch (final NumberFormatException ignored) { return none(); } - } }; } /** * A function that returns true if the given double is greater than zero. */ - public static final F gtZero = new F() { - public Boolean f(final Double i) { - return Double.compare(i, 0) > 0; - } - }; + public static final F gtZero = i -> Double.compare(i, 0) > 0; /** * A function that returns true if the given double is greater than or equal to zero. */ - public static final F gteZero = new F() { - public Boolean f(final Double i) { - return Double.compare(i, 0) >= 0; - } - }; + public static final F gteZero = i -> Double.compare(i, 0) >= 0; /** * A function that returns true if the given double is less than zero. */ - public static final F ltZero = new F() { - public Boolean f(final Double i) { - return Double.compare(i, 0) < 0; - } - }; + public static final F ltZero = i -> Double.compare(i, 0) < 0; /** * A function that returns true if the given double is less than or equal to zero. */ - public static final F lteZero = new F() { - public Boolean f(final Double i) { - return Double.compare(i, 0) <= 0; - } - }; + public static final F lteZero = i -> Double.compare(i, 0) <= 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 a9434186..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() { @@ -37,56 +35,32 @@ private Integers() { /** * Curried Integer subtraction. */ - public static final F> subtract = curry(new F2() { - public Integer f(final Integer x, final Integer y) { - return x - y; - } - }); + public static final F> subtract = curry((x, y) -> x - y); /** * Negation. */ - public static final F negate = new F() { - public Integer f(final Integer x) { - return x * -1; - } - }; + public static final F negate = x -> x * -1; /** * Absolute value. */ - public static final F abs = new F() { - public Integer f(final Integer x) { - return abs(x); - } - }; + public static final F abs = Math::abs; /** * Remainder. */ - public static final F> remainder = curry(new F2() { - public Integer f(final Integer a, final Integer b) { - return a % b; - } - }); + public static final F> remainder = curry((a, b) -> a % b); /** * Power. */ - public static final F> power = curry(new F2() { - public Integer f(final Integer a, final Integer b) { - return (int) StrictMath.pow(a, b); - } - }); + public static final F> power = curry((a, b) -> (int) StrictMath.pow(a, b)); /** * Evenness. */ - public static final F even = new F() { - public Boolean f(final Integer i) { - return i % 2 == 0; - } - }; + public static final F even = i -> i % 2 == 0; /** * Sums a list of integers. @@ -114,49 +88,32 @@ public static int product(final List ints) { * @return A function that converts strings to integers. */ public static F> fromString() { - return new F>() { - public Option f(final String s) { + return s -> { try { return some(Integer.valueOf(s)); } catch (final NumberFormatException ignored) { return none(); } - } }; } /** * A function that returns true if the given integer is greater than zero. */ - public static final F gtZero = new F() { - public Boolean f(final Integer i) { - return i > 0; - } - }; + public static final F gtZero = i -> i > 0; /** * A function that returns true if the given integer is greater than or equal to zero. */ - public static final F gteZero = new F() { - public Boolean f(final Integer i) { - return i >= 0; - } - }; + public static final F gteZero = i -> i >= 0; /** * A function that returns true if the given integer is less than zero. */ - public static final F ltZero = new F() { - public Boolean f(final Integer i) { - return i < 0; - } - }; + public static final F ltZero = i -> i < 0; /** * A function that returns true if the given integer is less than or equal to zero. */ - public static final F lteZero = new F() { - public Boolean f(final Integer i) { - return i <= 0; - } - }; + public static final F lteZero = i -> i <= 0; + } diff --git a/core/src/main/java/fj/function/Longs.java b/core/src/main/java/fj/function/Longs.java index d2d765a1..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() { @@ -31,36 +34,55 @@ private Longs() { /** * Curried Long subtraction. */ - public static final F> subtract = curry(new F2() { - public Long f(final Long x, final Long y) { - return x - y; - } - }); + public static final F> subtract = curry((x, y) -> x - y); /** * Negation. */ - public static final F negate = new F() { - public Long f(final Long x) { - return x * -1L; - } - }; + public static final F negate = x -> x * -1L; /** * Absolute value. */ - public static final F abs = new F() { - public Long f(final Long x) { - return abs(x); - } - }; + public static final F abs = Math::abs; /** * Remainder. */ - public static final F> remainder = curry(new F2() { - public Long f(final Long a, final Long b) { - return a % b; - } - }); + 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 c1381f06..d9815248 100644 --- a/core/src/main/java/fj/function/Strings.java +++ b/core/src/main/java/fj/function/Strings.java @@ -1,23 +1,42 @@ package fj.function; import fj.F; -import fj.F2; +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"); + + /** + * This function checks if a given String is neither null nor empty. + * + * @see #isNullOrEmpty + */ + public static final F isNotNullOrEmpty = a -> a != null && a.length() > 0; + + /** + * This function checks if a given String is null or empty ({@link String#isEmpty()}). + * + * @see #isNotNullOrEmpty + */ + public static final F isNullOrEmpty = a -> a == null || a.length() == 0; + + /** * This function checks if a given String contains any non-whitespace character * (according to {@link Character#isWhitespace(char)}) and if it's also not * null and not empty (""). @@ -26,13 +45,7 @@ private Strings() { * @see Character#isWhitespace(char) * @see Characters#isWhitespace */ - public static final F isNotNullOrBlank = new F() { - @Override - public Boolean f(final String a) { - return isNotNullOrEmpty.f(a) && Stream.fromString(a).find(not(isWhitespace)).isSome(); - } - }; - + public static final F isNotNullOrBlank = a -> isNotNullOrEmpty.f(a) && Stream.fromString(a).find(not(isWhitespace)).isSome(); /** * This function checks if a given String is whitespace (according to {@link Character#isWhitespace(char)}), * empty ("") or null. @@ -41,73 +54,50 @@ public Boolean f(final String a) { * @see Character#isWhitespace(char) * @see Characters#isWhitespace */ - public static final F isNullOrBlank = new F() { - @Override - public Boolean f(final String a) { - return isNullOrEmpty.f(a) || Stream.fromString(a).find(not(isWhitespace)).isNone(); - } - }; - - /** - * This function checks if a given String is neither null nor empty. - * - * @see #isNullOrEmpty - */ - public static final F isNotNullOrEmpty = new F() { - @Override - public Boolean f(final String a) { - return a != null && a.length() > 0; - } - }; - - /** - * This function checks if a given String is null or empty ({@link String#isEmpty()}). - * - * @see #isNotNullOrEmpty - */ - public static final F isNullOrEmpty = new F() { - @Override - public Boolean f(final String a) { - return a == null || a.length() == 0; - } - }; + public static final F isNullOrBlank = a -> isNullOrEmpty.f(a) || Stream.fromString(a).find(not(isWhitespace)).isNone(); /** * A curried version of {@link String#isEmpty()}. */ - public static final F isEmpty = new F() { - public Boolean f(final String s) { - return s.length() == 0; - } - }; + public static final F isEmpty = s -> s.length() == 0; /** * A curried version of {@link String#length()}. */ - public static final F length = new F() { - public Integer f(final String s) { - return s.length(); - } - }; + public static final F length = String::length; /** * A curried version of {@link String#contains(CharSequence)}. * The function returns true if the second argument contains the first. */ - public static final F> contains = curry(new F2() { - public Boolean f(final String s1, final String s2) { - return s2.contains(s1); - } - }); + public static final F> contains = curry((s1, s2) -> s2.contains(s1)); /** * A curried version of {@link String#matches(String)}. * The function returns true if the second argument matches the first. */ - public static final F> matches = curry(new F2() { - public Boolean f(final String s1, final String s2) { - return s2.matches(s1); - } - }); + public static final F> matches = curry((s1, s2) -> s2.matches(s1)); + + public static List lines(String s) { + return List.list(lineSeparatorPattern.split(s)); + } + + public static F> lines() { + return Strings::lines; + } + + public static String unlines(List list) { + StringBuilder sb = new StringBuilder(); + list.intersperse(lineSeparator).foreachDoEffect(sb::append); + return sb.toString(); + } + + public static F, String> unlines() { + 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 9898e2af..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 - * @version %build.number% + * @see fj.Try#f(Try0) */ 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 9bf3d068..53f5d048 100644 --- a/core/src/main/java/fj/function/Try1.java +++ b/core/src/main/java/fj/function/Try1.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try1) */ public interface Try1 { diff --git a/core/src/main/java/fj/function/Try2.java b/core/src/main/java/fj/function/Try2.java index 15dc25fe..24e6a099 100644 --- a/core/src/main/java/fj/function/Try2.java +++ b/core/src/main/java/fj/function/Try2.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F2. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try2) */ public interface Try2 { diff --git a/core/src/main/java/fj/function/Try3.java b/core/src/main/java/fj/function/Try3.java index 4d3ed383..7c0de0eb 100644 --- a/core/src/main/java/fj/function/Try3.java +++ b/core/src/main/java/fj/function/Try3.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F3. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try3) */ public interface Try3 { diff --git a/core/src/main/java/fj/function/Try4.java b/core/src/main/java/fj/function/Try4.java index f919ccbb..d2b79d2c 100644 --- a/core/src/main/java/fj/function/Try4.java +++ b/core/src/main/java/fj/function/Try4.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F4. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try4) */ public interface Try4 { diff --git a/core/src/main/java/fj/function/Try5.java b/core/src/main/java/fj/function/Try5.java index cd9fb31f..cbc1a93d 100644 --- a/core/src/main/java/fj/function/Try5.java +++ b/core/src/main/java/fj/function/Try5.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F5. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try5) */ public interface Try5 { diff --git a/core/src/main/java/fj/function/Try6.java b/core/src/main/java/fj/function/Try6.java index c12d1672..39bffbcb 100644 --- a/core/src/main/java/fj/function/Try6.java +++ b/core/src/main/java/fj/function/Try6.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F6. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try6) */ public interface Try6 { diff --git a/core/src/main/java/fj/function/Try7.java b/core/src/main/java/fj/function/Try7.java index 69c4dddd..ab56ffcd 100644 --- a/core/src/main/java/fj/function/Try7.java +++ b/core/src/main/java/fj/function/Try7.java @@ -6,8 +6,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F7. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try7) */ public interface Try7 { diff --git a/core/src/main/java/fj/function/Try8.java b/core/src/main/java/fj/function/Try8.java index 51e61990..1486de3c 100644 --- a/core/src/main/java/fj/function/Try8.java +++ b/core/src/main/java/fj/function/Try8.java @@ -5,8 +5,7 @@ * * Used to instantiate a lambda that may throw an Exception before converting to an F8. * - * @see fj.Try#f - * @version %build.number% + * @see fj.Try#f(Try8) */ 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 fdece88e..ed0e7b53 100644 --- a/core/src/main/java/fj/function/Visitor.java +++ b/core/src/main/java/fj/function/Visitor.java @@ -2,22 +2,19 @@ import fj.Equal; import fj.F; -import fj.F2; +import fj.F0; import fj.Function; import fj.Monoid; import fj.P1; import fj.P2; import fj.data.List; import fj.data.Option; - import static fj.Function.compose; import static fj.Function.curry; import static fj.data.List.lookup; /** * The essence of the visitor design pattern expressed polymorphically. - * - * @version %build.number% */ public final class Visitor { private Visitor() { @@ -31,7 +28,7 @@ private Visitor() { * @param def The default value if no value is found in the list. * @return The first value available in the given list of optional values. If none is found return the given default value. */ - public static X findFirst(final List> values, final P1 def) { + public static X findFirst(final List> values, final F0 def) { return Monoid.firstOptionMonoid().sumLeft(values).orSome(def); } @@ -42,8 +39,8 @@ public static X findFirst(final List> values, final P1 def) { * @param def The default value if no value is found in the list. * @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 P1 def) { - return findFirst(values.map(Option.fromNull()), def); + public static X nullablefindFirst(final List values, final F0 def) { + return findFirst(values.map(Option.fromNull()), def); } /** @@ -56,8 +53,8 @@ public static X nullablefindFirst(final List values, final P1 def) { * @return The first value found in the list of visitors after application of the given value, otherwise returns the * given default. */ - public static B visitor(final List>> visitors, final P1 def, final A value) { - return findFirst(visitors.map(Function.>apply(value)), def); + public static B visitor(final List>> visitors, final F0 def, final A value) { + return findFirst(visitors.map(Function.apply(value)), def); } /** @@ -70,12 +67,8 @@ public static B visitor(final List>> visitors, final P1 * @return The first value found in the list of visitors after application of the given value, otherwise returns the * given default. */ - public static B nullableVisitor(final List> visitors, final P1 def, final A value) { - return visitor(visitors.map(new F, F>>() { - public F> f(final F k) { - return compose(Option.fromNull(), k); - } - }), def, value); + public static B nullableVisitor(final List> visitors, final F0 def, final A value) { + return visitor(visitors.map(k -> compose(Option.fromNull(), k)), def, value); } /** @@ -87,11 +80,7 @@ public F> f(final F k) { * @return A function that can be applied to a default value (there is no association) and an associated key. */ public static F> association(final List> x, final Equal eq) { - return curry(new F2() { - public B f(final B def, final A a) { - return lookup(eq, x, a).orSome(def); - } - }); + return curry((def, a) -> lookup(eq, x, a).orSome(def)); } /** @@ -103,10 +92,6 @@ public B f(final B def, final A a) { * @return A function that can be applied to a default value (there is no association) and an associated key. */ public static F, F> associationLazy(final List> x, final Equal eq) { - return curry(new F2, A, B>() { - public B f(final P1 def, final A a) { - return lookup(eq, x, a).orSome(def); - } - }); + return curry((def, a) -> lookup(eq, x, a).orSome(def)); } } 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 e74c697f..b5753f23 100644 --- a/core/src/main/java/fj/parser/Parser.java +++ b/core/src/main/java/fj/parser/Parser.java @@ -1,11 +1,8 @@ package fj.parser; -import fj.F; +import fj.*; + import static fj.P.p; -import fj.P1; -import fj.Semigroup; -import fj.Unit; -import fj.Digit; import static fj.Unit.unit; import fj.data.List; import static fj.data.List.cons_; @@ -17,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; @@ -45,15 +40,7 @@ public Validation> parse(final I i) { * @return A parser with the new input type. */ public Parser xmap(final F f, final F g) { - return parser(new F>>() { - public Validation> f(final Z z) { - return parse(g.f(z)).map(new F, Result>() { - public Result f(final Result r) { - return r.mapRest(f); - } - }); - } - }); + return parser(z -> parse(g.f(z)).map(r -> r.mapRest(f))); } /** @@ -63,15 +50,7 @@ public Result f(final Result r) { * @return A parser with the new result type. */ public Parser map(final F f) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).map(new F, Result>() { - public Result f(final Result r) { - return r.mapValue(f); - } - }); - } - }); + return parser(i -> parse(i).map(r -> r.mapValue(f))); } /** @@ -82,18 +61,12 @@ public Result f(final Result r) { * @return A parser that fails with the given error if the result value does not meet the given predicate. */ public Parser filter(final F f, final E e) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).bind(new F, Validation>>() { - public Validation> f(final Result r) { - final A v = r.value(); - return f.f(v) ? - Validation.>success(result(r.rest(), v)) : - Validation.>fail(e); - } - }); - } - }); + return parser(i -> parse(i).bind(r -> { + final A v = r.value(); + return f.f(v) ? + Validation.>success(result(r.rest(), v)) : + Validation.>fail(e); + })); } /** @@ -103,15 +76,7 @@ public Validation> f(final Result r) { * @return A new parser after performing the map, then final join. */ public Parser bind(final F> f) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).bind(new F, Validation>>() { - public Validation> f(final Result r) { - return f.f(r.value()).parse(r.rest()); - } - }); - } - }); + return parser(i -> parse(i).bind(r -> f.f(r.value()).parse(r.rest()))); } /** @@ -233,11 +198,7 @@ public Parser bind(final Parser pb, final Parser Parser sequence(final Parser p) { - return bind(new F>() { - public Parser f(final A a) { - return p; - } - }); + return bind(a -> p); } /** @@ -247,11 +208,7 @@ public Parser f(final A a) { * @return A new parser after function application. */ public Parser apply(final Parser, E> p) { - return p.bind(new F, Parser>() { - public Parser f(final F f) { - return map(f); - } - }); + return p.bind(this::map); } /** @@ -260,12 +217,8 @@ public Parser f(final F f) { * @param alt The parser to try if this parser fails. * @return A parser that tries this parser and if it fails, then tries the given parser. */ - public Parser or(final P1> alt) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).f().sequence(alt._1().parse(i)); - } - }); + public Parser or(final F0> alt) { + return parser(i -> parse(i).f().sequence(alt.f().parse(i))); } /** @@ -286,16 +239,8 @@ public Parser or(final Parser alt) { * @param s The semigroup to append error messages if both parsers fail. * @return A parser that tries this parser and if it fails, then tries the given parser. */ - public Parser or(final P1> alt, final Semigroup s) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).f().bind(new F>>() { - public Validation> f(final E e) { - return alt._1().parse(i).f().map(s.sum(e)); - } - }); - } - }); + public Parser or(final F0> alt, final Semigroup s) { + return parser(i -> parse(i).f().bind(e -> alt.f().parse(i).f().map(s.sum(e)))); } /** @@ -316,14 +261,10 @@ public Parser or(final Parser alt, final Semigroup s) { * @param e The error message to fail with if this parser succeeds. * @return A parser that negates this parser. */ - public Parser not(final P1 e) { - return parser(new F>>() { - public Validation> f(final I i) { - return parse(i).isFail() ? - Validation.>success(result(i, unit())) : - Validation.>fail(e._1()); - } - }); + public Parser not(final F0 e) { + return parser(i -> parse(i).isFail() ? + Validation.success(result(i, unit())) : + Validation.fail(e.f())); } /** @@ -342,11 +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(new P1, E>>() { - public Parser, E> _1() { - return value(Stream.nil()); - } - }); + return repeat1().or(() -> value(Stream.nil())); } /** @@ -355,15 +292,7 @@ public Parser, E> _1() { * @return A parser that repeats application of this parser one or many times. */ public Parser, E> repeat1() { - return bind(new F, E>>() { - public Parser, E> f(final A a) { - return repeat().map(new F, Stream>() { - public Stream f(final Stream as) { - return as.cons(a); - } - }); - } - }); + return bind(a -> repeat().map(as -> as.cons(a))); } /** @@ -373,11 +302,7 @@ public Stream f(final Stream as) { * @return A new parser with a new error type. */ public Parser mapError(final F f) { - return parser(new F>>() { - public Validation> f(final I i) { - return Parser.this.f.f(i).f().map(f); - } - }); + return parser(i -> Parser.this.f.f(i).f().map(f)); } /** @@ -387,7 +312,7 @@ public Validation> f(final I i) { * @return A parser that computes using the given function. */ public static Parser parser(final F>> f) { - return new Parser(f); + return new Parser<>(f); } /** @@ -397,11 +322,7 @@ public static Parser parser(final F Parser value(final A a) { - return parser(new F>>() { - public Validation> f(final I i) { - return success(result(i, a)); - } - }); + return parser(i -> success(result(i, a))); } /** @@ -411,11 +332,7 @@ public Validation> f(final I i) { * @return A parser that always fails with the given error. */ public static Parser fail(final E e) { - return parser(new F>>() { - public Validation> f(final I i) { - return Validation.fail(e); - } - }); + return parser(i -> Validation.fail(e)); } /** @@ -426,12 +343,8 @@ public Validation> f(final I i) { */ public static Parser, E> sequence(final List> ps) { return ps.isEmpty() ? - Parser., E>value(List.nil()) : - ps.head().bind(new F, E>>() { - public Parser, E> f(final A a) { - return sequence(ps.tail()).map(cons_(a)); - } - }); + Parser.value(List.nil()) : + ps.head().bind(a -> sequence(ps.tail()).map(cons_(a))); } /** @@ -448,14 +361,10 @@ private StreamParser() { * @param e The error to fail with if no element is available. * @return A parser that produces an element from the stream if it is available and fails otherwise. */ - public static Parser, I, E> element(final P1 e) { - return parser(new F, Validation, I>>>() { - public Validation, I>> f(final Stream is) { - return is.isEmpty() ? - Validation., I>>fail(e._1()) : - Validation., I>>success(result(is.tail()._1(), is.head())); - } - }); + public static Parser, I, E> element(final F0 e) { + return parser(is -> is.isEmpty() ? + Validation.fail(e.f()) : + Validation.success(result(is.tail()._1(), is.head()))); } /** @@ -476,15 +385,11 @@ public static Parser, I, E> element(final E e) { * @param f The predicate that the element should satisfy. * @return A parser that produces an element from the stream that satisfies the given predicate, or fails. */ - public static Parser, I, E> satisfy(final P1 missing, final F sat, + public static Parser, I, E> satisfy(final F0 missing, final F sat, final F f) { - return StreamParser.element(missing).bind(new F, I, E>>() { - public Parser, I, E> f(final I x) { - return f.f(x) ? - Parser., I, E>value(x) : - Parser., I, E>fail(sat.f(x)); - } - }); + return StreamParser.element(missing).bind(x -> f.f(x) ? + Parser.value(x) : + Parser.fail(sat.f(x))); } /** @@ -514,7 +419,7 @@ private CharsParser() { * @param e The error to fail with if a character is unavailable. * @return A parser that produces a character if one is available or fails with the given error. */ - public static Parser, Character, E> character(final P1 e) { + public static Parser, Character, E> character(final F0 e) { return StreamParser.element(e); } @@ -536,13 +441,9 @@ public static Parser, Character, E> character(final E e) { * @param c The character to produce in the parser. * @return A parser that produces the given character or fails otherwise. */ - public static Parser, Character, E> character(final P1 missing, final F sat, + public static Parser, Character, E> character(final F0 missing, final F sat, final char c) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character x) { - return x == c; - } - }); + return StreamParser.satisfy(missing, sat, x -> x == c); } /** @@ -565,10 +466,10 @@ public static Parser, Character, E> character(final E miss * @param n The number of characters to produce in the parse result. * @return A parser that produces the given number of characters, or fails with the given error. */ - public static Parser, Stream, E> characters(final P1 missing, final int n) { + 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_()); } /** @@ -590,12 +491,12 @@ public static Parser, Stream, E> characters(fin * @param cs The stream of characters to produce. * @return A parser that produces the given stream of characters or fails otherwise. */ - public static Parser, Stream, E> characters(final P1 missing, + public static Parser, Stream, E> characters(final F0 missing, 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_()); } /** @@ -620,13 +521,9 @@ public static Parser, Stream, E> characters(fin * @param s The string to produce. * @return A parser that produces the given string or fails otherwise. */ - public static Parser, String, E> string(final P1 missing, final F sat, + public static Parser, String, E> string(final F0 missing, final F sat, final String s) { - return characters(missing, sat, List.fromString(s).toStream()).map(new F, String>() { - public String f(final Stream cs) { - return List.asString(cs.toList()); - } - }); + return characters(missing, sat, List.fromString(s).toStream()).map(cs -> List.asString(cs.toList())); } /** @@ -649,16 +546,8 @@ public static Parser, String, E> string(final E missing, f * @param sat The error if the produced character is not a digit. * @return A parser that produces a digit (0 to 9). */ - public static Parser, Digit, E> digit(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isDigit(c); - } - }).map(new F() { - public Digit f(final Character c) { - return Digit.fromChar(c).some(); - } - }); + public static Parser, Digit, E> digit(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isDigit).map(c1 -> Digit.fromChar(c1).some()); } /** @@ -680,12 +569,8 @@ public static Parser, Digit, E> digit(final E missing, fin * @return A parser that produces a lower-case character. * @see Character#isLowerCase(char) */ - public static Parser, Character, E> lower(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isLowerCase(c); - } - }); + public static Parser, Character, E> lower(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isLowerCase); } /** @@ -708,12 +593,8 @@ public static Parser, Character, E> lower(final E missing, * @return A parser that produces a upper-case character. * @see Character#isUpperCase(char) */ - public static Parser, Character, E> upper(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isUpperCase(c); - } - }); + public static Parser, Character, E> upper(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isUpperCase); } /** @@ -736,12 +617,8 @@ public static Parser, Character, E> upper(final E missing, * @return A parser that produces a defined character. * @see Character#isDefined(char) */ - public static Parser, Character, E> defined(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isDefined(c); - } - }); + public static Parser, Character, E> defined(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isDefined); } /** @@ -764,13 +641,9 @@ public static Parser, Character, E> defined(final E missin * @return A parser that produces a high-surrogate character. * @see Character#isHighSurrogate(char) */ - public static Parser, Character, E> highSurrogate(final P1 missing, + public static Parser, Character, E> highSurrogate(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isHighSurrogate(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isHighSurrogate); } /** @@ -794,13 +667,9 @@ public static Parser, Character, E> highSurrogate(final E * @return A parser that produces an identifier-ignorable character. * @see Character#isIdentifierIgnorable(char) */ - public static Parser, Character, E> identifierIgnorable(final P1 missing, + public static Parser, Character, E> identifierIgnorable(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isIdentifierIgnorable(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isIdentifierIgnorable); } /** @@ -824,13 +693,9 @@ public static Parser, Character, E> identifierIgnorable(fi * @return A parser that produces an ISO control character. * @see Character#isISOControl(char) */ - public static Parser, Character, E> isoControl(final P1 missing, + public static Parser, Character, E> isoControl(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isISOControl(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isISOControl); } /** @@ -853,13 +718,9 @@ public static Parser, Character, E> isoControl(final E mis * @return A parser that produces a Java identifier part character. * @see Character#isJavaIdentifierPart(char) */ - public static Parser, Character, E> javaIdentifierPart(final P1 missing, + public static Parser, Character, E> javaIdentifierPart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isJavaIdentifierPart(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isJavaIdentifierPart); } /** @@ -883,13 +744,9 @@ public static Parser, Character, E> javaIdentifierPart(fin * @return A parser that produces a Java identifier start character. * @see Character#isJavaIdentifierStart(char) */ - public static Parser, Character, E> javaIdentifierStart(final P1 missing, + public static Parser, Character, E> javaIdentifierStart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isJavaIdentifierStart(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isJavaIdentifierStart); } /** @@ -913,12 +770,8 @@ public static Parser, Character, E> javaIdentifierStart(fi * @return A parser that produces an alpha character. * @see Character#isLetter(char) */ - public static Parser, Character, E> alpha(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isLetter(c); - } - }); + public static Parser, Character, E> alpha(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isLetter); } /** @@ -941,12 +794,8 @@ public static Parser, Character, E> alpha(final E missing, * @return A parser that produces an alpha-numeric character. * @see Character#isLetterOrDigit(char) */ - public static Parser, Character, E> alphaNum(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isLetterOrDigit(c); - } - }); + public static Parser, Character, E> alphaNum(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isLetterOrDigit); } /** @@ -969,13 +818,9 @@ public static Parser, Character, E> alphaNum(final E missi * @return A parser that produces a low-surrogate character. * @see Character#isLowSurrogate(char) */ - public static Parser, Character, E> lowSurrogate(final P1 missing, + public static Parser, Character, E> lowSurrogate(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isLowSurrogate(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isLowSurrogate); } /** @@ -998,12 +843,8 @@ public static Parser, Character, E> lowSurrogate(final E m * @return A parser that produces a mirrored character. * @see Character#isMirrored(char) */ - public static Parser, Character, E> mirrored(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isMirrored(c); - } - }); + public static Parser, Character, E> mirrored(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isMirrored); } /** @@ -1026,12 +867,8 @@ public static Parser, Character, E> mirrored(final E missi * @return A parser that produces a space character. * @see Character#isSpace(char) */ - public static Parser, Character, E> space(final P1 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isSpaceChar(c); - } - }); + public static Parser, Character, E> space(final F0 missing, final F sat) { + return StreamParser.satisfy(missing, sat, Character::isSpaceChar); } /** @@ -1054,13 +891,9 @@ public static Parser, Character, E> space(final E missing, * @return A parser that produces a title-case character. * @see Character#isTitleCase(char) */ - public static Parser, Character, E> titleCase(final P1 missing, + public static Parser, Character, E> titleCase(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isTitleCase(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isTitleCase); } /** @@ -1083,13 +916,9 @@ public static Parser, Character, E> titleCase(final E miss * @return A parser that produces a unicode identifier part character. * @see Character#isUnicodeIdentifierPart(char) */ - public static Parser, Character, E> unicodeIdentiferPart(final P1 missing, + public static Parser, Character, E> unicodeIdentiferPart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isUnicodeIdentifierPart(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isUnicodeIdentifierPart); } /** @@ -1113,13 +942,9 @@ public static Parser, Character, E> unicodeIdentiferPart(f * @return A parser that produces a unicode identifier start character. * @see Character#isUnicodeIdentifierStart(char) */ - public static Parser, Character, E> unicodeIdentiferStart(final P1 missing, + public static Parser, Character, E> unicodeIdentiferStart(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return Character.isUnicodeIdentifierStart(c); - } - }); + return StreamParser.satisfy(missing, sat, Character::isUnicodeIdentifierStart); } /** @@ -1143,13 +968,9 @@ public static Parser, Character, E> unicodeIdentiferStart( * @return A parser that produces a white-space character. * @see Character#isWhitespace(char) */ - public static Parser, Character, E> whitespace(final P1 missing, + public static Parser, Character, E> whitespace(final F0 missing, final F sat) { - return StreamParser.satisfy(missing, sat, new F() { - public Boolean f(final Character c) { - return 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 b088b4c6..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,11 +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 new F, Result>() { - public Result f(final F f) { - return mapRest(f); - } - }; + return this::mapRest; } /** @@ -78,11 +91,7 @@ public Result mapValue(final F f) { * @return A first-class function mapping across the parse value. */ public F, Result> mapValue() { - return new F, Result>() { - public Result f(final F f) { - return mapValue(f); - } - }; + return this::mapValue; } /** @@ -102,11 +111,7 @@ public Result bimap(final F f, final F g) { * @return A first-class bifunctor map. */ public F, F, Result>> bimap() { - return curry(new F2, F, Result>() { - public Result f(final F f, final F g) { - return bimap(f, g); - } - }); + return curry(this::bimap); } /** @@ -145,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); } /** @@ -154,10 +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(new F2>() { - public Result f(final I i, final A a) { - return 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/main/java/fj/test/Arbitrary.java b/core/src/main/java/fj/test/Arbitrary.java deleted file mode 100644 index e50a3d78..00000000 --- a/core/src/main/java/fj/test/Arbitrary.java +++ /dev/null @@ -1,1655 +0,0 @@ -package fj.test; - -import fj.F; -import fj.F2; -import fj.F3; -import fj.F4; -import fj.F5; -import fj.F6; -import fj.F7; -import fj.F8; -import fj.Function; -import static fj.Function.compose; -import static fj.P.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.data.Array; -import fj.data.Either; -import static fj.data.Either.left; -import static fj.data.Either.right; -import static fj.data.Enumerator.charEnumerator; -import fj.data.List; -import static fj.data.List.asString; -import static fj.data.List.list; -import fj.data.Option; -import static fj.data.Option.some; -import fj.data.Stream; -import fj.function.Effect1; - -import static fj.data.Stream.range; -import static fj.test.Gen.choose; -import static fj.test.Gen.elements; -import static fj.test.Gen.fail; -import static fj.test.Gen.frequency; -import static fj.test.Gen.listOf; -import static fj.test.Gen.oneOf; -import static fj.test.Gen.promote; -import static fj.test.Gen.sized; -import static fj.test.Gen.value; - -import static java.lang.Math.abs; -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.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; -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; - -/** - * The type used to generate arbitrary values of the given type parameter (A). Common - * arbitrary implementations are provided. - * - * @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); - } - - /** - * An arbitrary for functions. - * - * @param c The coarbitrary 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); - } - })); - } - - /** - * An arbitrary for functions. - * - * @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())); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-2. - * - * @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())))); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-3. - * - * @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(), - compose( - 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 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())); - } - - /** - * An arbitrary for function-4. - * - * @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())))))); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-5. - * - * @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()))))))); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-6. - * - * @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())))))))); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-7. - * - * @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()))))))))); - } - - /** - * 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 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())); - } - - /** - * An arbitrary for function-8. - * - * @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())))))))))); - } - - /** - * An arbitrary implementation for boolean values. - */ - public static final Arbitrary arbBoolean = arbitrary(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); - } - })); - - /** - * 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))); - } - })); - - /** - * 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; - } - }; - } - })); - - /** - * 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))); - } - })); - - /** - * 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(); - } - })); - - /** - * 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))); - } - })); - - /** - * 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(); - } - })); - - /** - * 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))); - } - })); - - /** - * 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(); - } - })); - - /** - * 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))); - } - })); - - /** - * 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); - } - })); - - /** - * 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))); - } - })); - - /** - * 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(); - } - })); - - /** - * 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))); - } - })); - - /** - * 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); - } - })); - - /** - * 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); - } - })); - } - })); - - /** - * An arbitrary implementation for string values with alpha-numeric characters. - */ - public static final Arbitrary arbAlphaNumString = - arbitrary(arbList(arbitrary(elements(range(charEnumerator, 'a', 'z').append( - range(charEnumerator, 'A', 'Z')).append( - range(charEnumerator, '0', '9')).toArray().array(Character[].class)))).gen.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); - } - })); - - /** - * 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); - } - })); - - /** - * Returns an arbitrary implementation for generators. - * - * @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); - } - })); - } - - /** - * Returns an arbitrary implementation for optional values. - * - * @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); - } - })); - } - - /** - * Returns an arbitrary implementation for the disjoint union. - * - * @param aa An arbitrary implementation for the type over which one side of the disjoint union is - * defined. - * @param ab An arbitrary implementation for the type over which one side of the disjoint union is - * 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))); - } - - /** - * Returns an arbitrary implementation for lists. - * - * @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)); - } - - /** - * Returns an arbitrary implementation for streams. - * - * @param aa An arbitrary implementation for the type over which the stream is defined. - * @return An arbitrary implementation for streams. - */ - public static Arbitrary> arbStream(final Arbitrary aa) { - return arbitrary(arbList(aa).gen.map(new F, Stream>() { - public Stream f(final List as) { - return as.toStream(); - } - })); - } - - /** - * Returns an arbitrary implementation for arrays. - * - * @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(); - } - })); - } - - /** - * Returns an arbitrary implementation for throwables. - * - * @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); - } - })); - } - - /** - * An arbitrary implementation for throwables. - */ - public static final Arbitrary arbThrowable = arbThrowable(arbString); - - // BEGIN java.util - - /** - * Returns an arbitrary implementation for array lists. - * - * @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()); - } - })); - } - - /** - * 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; - } - })); - - /** - * 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; - } - })); - - /** - * 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); - } - })); - - /** - * Returns an arbitrary implementation for a Java enumeration. - * - * @param clazz The type of enum to return an arbtrary of. - * @return An arbitrary for instances of the supplied enum type. - */ - public static > Arbitrary arbEnumValue(final Class clazz) { - return arbitrary(Gen.elements(clazz.getEnumConstants())); - } - - /** - * Returns an arbitrary implementation for enum maps. - * - * @param ak An arbitrary implementation for the type over which the enum map's keys are defined. - * @param av An arbitrary implementation for the type over which the enum map's values are - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for enum sets. - * - * @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()); - } - })); - } - - /** - * 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; - } - })); - - /** - * Returns an arbitrary implementation for hash maps. - * - * @param ak An arbitrary implementation for the type over which the hash map's keys are defined. - * @param av An arbitrary implementation for the type over which the hash map's values are - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for hash sets. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for hash tables. - * - * @param ak An arbitrary implementation for the type over which the hash table's keys are - * defined. - * @param av An arbitrary implementation for the type over which the hash table's values are - * 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; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for identity hash maps. - * - * @param ak An arbitrary implementation for the type over which the identity hash map's keys are - * defined. - * @param av An arbitrary implementation for the type over which the identity hash map's values - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for linked hash maps. - * - * @param ak An arbitrary implementation for the type over which the linked hash map's keys are - * defined. - * @param av An arbitrary implementation for the type over which the linked hash map's values are - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for hash sets. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for linked lists. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for priority queues. - * - * @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()); - } - })); - } - - /** - * 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)); - } - - return p; - } - })); - - /** - * Returns an arbitrary implementation for stacks. - * - * @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; - } - })); - } - - /** - * Returns an arbitrary implementation for tree maps. - * - * @param ak An arbitrary implementation for the type over which the tree map's keys are defined. - * @param av An arbitrary implementation for the type over which the tree map's values are - * defined. - * @return An arbitrary implementation for tree maps. - */ - public static Arbitrary> arbTreeMap(final Arbitrary ak, final Arbitrary av) { - return arbitrary(arbHashtable(ak, av).gen.map(new F, TreeMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public TreeMap f(final Hashtable ht) { - return new TreeMap(ht); - } - })); - } - - /** - * Returns an arbitrary implementation for tree sets. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for vectors. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for weak hash maps. - * - * @param ak An arbitrary implementation for the type over which the weak hash map's keys are - * defined. - * @param av An arbitrary implementation for the type over which the weak hash map's values are - * 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); - } - })); - } - - // END java.util - - // BEGIN java.util.concurrent - - /** - * Returns an arbitrary implementation for array blocking queues. - * - * @param aa An arbitrary implementation for the type over which the array blocking queue is - * 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()); - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for concurrent hash maps. - * - * @param ak An arbitrary implementation for the type over which the concurrent hash map's keys - * are defined. - * @param av An arbitrary implementation for the type over which the concurrent hash map's values - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for concurrent linked queues. - * - * @param aa An arbitrary implementation for the type over which the concurrent linked queue is - * 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()); - } - })); - } - - /** - * Returns an arbitrary implementation for copy-on-write array lists. - * - * @param aa An arbitrary implementation for the type over which the copy-on-write array list is - * 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()); - } - })); - } - - /** - * Returns an arbitrary implementation for copy-on-write array sets. - * - * @param aa An arbitrary implementation for the type over which the copy-on-write array set is - * 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()); - } - })); - } - - /** - * Returns an arbitrary implementation for delay queues. - * - * @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()); - } - })); - } - - /** - * Returns an arbitrary implementation for linked blocking queues. - * - * @param aa An arbitrary implementation for the type over which the linked blocking queue is - * 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()); - } - })); - } - - /** - * Returns an arbitrary implementation for priority blocking queues. - * - * @param aa An arbitrary implementation for the type over which the priority blocking queue is - * 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()); - } - })); - } - - /** - * Returns an arbitrary implementation for priority blocking queues. - * - * @param aa An arbitrary implementation for the type over which the priority blocking queue is - * 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; - } - }; - } - })); - } - - // END java.util.concurrent - - // BEGIN java.sql - - /** - * 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); - } - })); - - /** - * 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); - } - })); - } - - /** - * Returns an arbitrary implementation for product-2 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-2 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-2 is - * 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); - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-3 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-3 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-3 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-3 is - * 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); - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-4 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-4 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-4 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-4 is - * defined. - * @param ad An arbitrary implementation for one of the types over which the product-4 is - * 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); - } - }; - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-5 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-5 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-5 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-5 is - * defined. - * @param ad An arbitrary implementation for one of the types over which the product-5 is - * defined. - * @param ae An arbitrary implementation for one of the types over which the product-5 is - * 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); - } - }; - } - }; - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-6 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-6 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-6 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-6 is - * defined. - * @param ad An arbitrary implementation for one of the types over which the product-6 is - * defined. - * @param ae An arbitrary implementation for one of the types over which the product-6 is - * defined. - * @param af An arbitrary implementation for one of the types over which the product-7 is - * 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-7 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param ad An arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param ae An arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param af An arbitrary implementation for one of the types over which the product-7 is - * defined. - * @param ag An arbitrary implementation for one of the types over which the product-8 is - * 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); - } - - /** - * Returns an arbitrary implementation for product-8 values. - * - * @param aa An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ab An Arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ac An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ad An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ae An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param af An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ag An arbitrary implementation for one of the types over which the product-8 is - * defined. - * @param ah An arbitrary implementation for one of the types over which the product-8 is - * 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); - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - }; - } - })); - } -} diff --git a/core/src/main/java/fj/test/Coarbitrary.java b/core/src/main/java/fj/test/Coarbitrary.java deleted file mode 100644 index d506e428..00000000 --- a/core/src/main/java/fj/test/Coarbitrary.java +++ /dev/null @@ -1,1176 +0,0 @@ -package fj.test; - -import fj.F; -import fj.F2; -import fj.F3; -import fj.F4; -import fj.F5; -import fj.F6; -import fj.F7; -import fj.F8; -import static fj.Function.curry; -import static fj.P.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.data.Array; -import static fj.data.Array.array; -import fj.data.Either; -import fj.data.List; -import static fj.data.List.fromString; -import static fj.data.List.nil; -import fj.data.Option; -import fj.data.Stream; - -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); - } - }; - } - - /** - * Co-maps this coarbitrary using the given function. - * - * @param f The function to co-map with. - * @return A co-mapped coarbitrary. - */ - public final Coarbitrary comap(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 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.comap(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/core/src/main/java/fj/test/Rand.java b/core/src/main/java/fj/test/Rand.java deleted file mode 100644 index 68c91062..00000000 --- a/core/src/main/java/fj/test/Rand.java +++ /dev/null @@ -1,156 +0,0 @@ -package fj.test; - -import fj.F; -import fj.data.Option; -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. - * - * @version %build.number% - */ -public final class Rand { - private final F, F>> f; - private final F, F>> g; - - private Rand(final F, F>> f, final F, F>> g) { - this.f = f; - this.g = g; - } - - /** - * Randomly chooses a value between the given range (inclusive). - * - * @param seed The seed to use for random generation. - * @param from The minimum value to choose. - * @param to The maximum value to choose. - * @return A random value in the given range. - */ - public int choose(final long seed, final int from, final int to) { - return f.f(some(seed)).f(from).f(to); - } - - /** - * Randomly chooses a value between the given range (inclusive). - * - * @param from The minimum value to choose. - * @param to The maximum value to choose. - * @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); - } - - /** - * Randomly chooses a value between the given range (inclusive). - * - * @param seed The seed to use for random generation. - * @param from The minimum value to choose. - * @param to The maximum value to choose. - * @return A random value in the given range. - */ - public double choose(final long seed, final double from, final double to) { - return g.f(some(seed)).f(from).f(to); - } - - /** - * Randomly chooses a value between the given range (inclusive). - * - * @param from The minimum value to choose. - * @param to The maximum value to choose. - * @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); - } - - /** - * Gives this random generator a new seed. - * - * @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); - } - }; - } - }; - } - }); - } - - /** - * Constructs a 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. - * @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); - } - - - private static final F fr = new F() { - public Random f(final Long x) { - return new Random(x); - } - }; - - /** - * A standard random generator that uses {@link Random}. - */ - public static final Rand standard = new Rand(new F, F>>() { - public F> f(final Option seed) { - return new F>() { - public F f(final Integer from) { - return new F() { - public Integer f(final Integer to) { - final int f = min(from, to); - final int t = max(from, to); - return f + seed.map(fr).orSome(new Random()).nextInt(t - f + 1); - } - }; - } - }; - } - }, new F, F>>() { - public F> f(final Option seed) { - return new F>() { - public F f(final Double from) { - return new F() { - public Double f(final Double to) { - final double f = min(from, to); - final double t = max(from, to); - return seed.map(fr).orSome(new Random()).nextDouble() * (t - f) + f; - } - }; - } - }; - } - }); -} 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/MemoisationTest.java b/core/src/test/java/fj/MemoisationTest.java deleted file mode 100644 index b3c2853b..00000000 --- a/core/src/test/java/fj/MemoisationTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package fj; - -import fj.test.Property; -import org.junit.Test; - -import static fj.test.Arbitrary.arbInteger; -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. - */ -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()); - }); - 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()); - }); - 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()); - } - - @Test - public void test3() { - final Property p = 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) -> { - 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) -> { - 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) -> { - 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) -> { - 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) -> { - 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/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 new file mode 100644 index 00000000..4d3c5c35 --- /dev/null +++ b/core/src/test/java/fj/P1Test.java @@ -0,0 +1,39 @@ +package fj; + +import org.junit.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public final class P1Test { + + @Test + public void bug105() throws Exception { + final P1 p1 = P.weakMemo(() -> "Foo"); + final AtomicInteger nullCounter = new AtomicInteger(); + ExecutorService executorService = Executors.newCachedThreadPool(); + + for (int i = 0; i < 10000; i++) { + executorService.submit(() -> { + if (p1._1() == null) { + nullCounter.incrementAndGet(); + } + }); + } + + executorService.shutdown(); + executorService.awaitTermination(10, TimeUnit.DAYS); + + org.junit.Assert.assertEquals("Race condition in P1.memo()", 0, nullCounter.get()); + } + + @Test + public void bug122() throws Exception { + final P1 p1a = P.lazy(() -> 1); + final P1 p1b = P.lazy(() -> 1); + + org.junit.Assert.assertTrue(p1a + " and " + p1b + " should be equal by Object.equals", p1a.equals(p1b)); + } +} 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 new file mode 100644 index 00000000..3d6d8354 --- /dev/null +++ b/core/src/test/java/fj/ShowTest.java @@ -0,0 +1,16 @@ +package fj; + +import fj.data.Array; +import org.junit.Test; + +import static fj.data.Array.array; +import static org.junit.Assert.assertTrue; + +public class ShowTest { + @Test + public void arrayShow() { + Array a = array(3, 5, 7); + String s = Show.arrayShow(Show.intShow).showS(a); + 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 new file mode 100644 index 00000000..11683fbf --- /dev/null +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -0,0 +1,20 @@ +package fj.data; + +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class ArrayTest { + + @Test + 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 new file mode 100644 index 00000000..e5dbeb12 --- /dev/null +++ b/core/src/test/java/fj/data/BooleansTest.java @@ -0,0 +1,79 @@ +package fj.data; + +import fj.F; +import fj.function.Booleans; +import org.junit.Assert; +import org.junit.Test; + +import static fj.P1.curry; +import static fj.data.List.list; +import static fj.function.Booleans.isnot; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class BooleansTest { + + @Test + public void testAnd(){ + F f1 = a -> a.startsWith("fj"); + F f2 = a -> a.contains("data"); + + F f3 = Booleans.and(f1, f2); + + Assert.assertTrue(f3.f("fj.data")); + Assert.assertTrue(f3.f("fj.data.Function")); + + } + + @Test + public void testOr(){ + F f1 = a -> a.startsWith("fj"); + F f2 = a -> a.startsWith("someOtherPackage"); + + F f3 = Booleans.or(f1, f2); + + Assert.assertTrue(f3.f("fj.data")); + Assert.assertTrue(f3.f("someOtherPackage.fj.data")); + Assert.assertFalse(f3.f("something.fj.data.Function")); + + } + + @Test + public void testContramap(){ + F f1 = a -> a.length() > 3; + F f2 = a -> a.toString(); + + F f3 = Booleans.contramap(f2, f1); + + Assert.assertTrue(f3.f(1000)); + Assert.assertFalse(f3.f(100)); + + } + + @SuppressWarnings("unchecked") + @Test + public void testAndAll(){ + F f1 = a -> a.endsWith("fj"); + F f2 = a -> a.startsWith("someOtherPackage"); + F f3 = a -> a.length() < 20; + + F f4 = Booleans.andAll(Stream.>stream(f1, f2, f3)); + + Assert.assertTrue(f4.f("someOtherPackage.fj")); + Assert.assertFalse(f4.f("otther")); + Assert.assertFalse(f4.f("someOtherPackage.fj.data.something.really.big")); + + } + + @SuppressWarnings("unchecked") + @Test + public void testIsNot(){ + F f1 = a -> a == 4; + List result = list("some", "come", "done!").filter(isnot(String::length, f1)); + + 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 new file mode 100644 index 00000000..060c5394 --- /dev/null +++ b/core/src/test/java/fj/data/LazyStringTest.java @@ -0,0 +1,19 @@ +package fj.data; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class LazyStringTest { + + @Test + public void testToString() { + Stream s = Stream.repeat(LazyString.str("abc")); + // FJ 4.3 blows the stack when printing infinite streams of lazy strings. + assertThat(s.toString(), is(equalTo("Cons(LazyString(a, ?), ?)"))); + } + +} diff --git a/core/src/test/java/fj/data/ListBufferTest.java b/core/src/test/java/fj/data/ListBufferTest.java new file mode 100644 index 00000000..784b9510 --- /dev/null +++ b/core/src/test/java/fj/data/ListBufferTest.java @@ -0,0 +1,22 @@ +package fj.data; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class ListBufferTest { + + @Test + public void testSnoc() { + // test case for #181 + List.Buffer buf = List.Buffer.empty(); + buf.snoc(1).snoc(2).snoc(3); + List list1 = buf.toList(); + buf.snoc(4); + List list2 = buf.toList(); + assertThat(list2, equalTo(Stream.range(1, 5).toList())); + } + +} diff --git a/core/src/test/java/fj/data/ListTest.java b/core/src/test/java/fj/data/ListTest.java new file mode 100644 index 00000000..720157e8 --- /dev/null +++ b/core/src/test/java/fj/data/ListTest.java @@ -0,0 +1,300 @@ +package fj.data; + +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.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +public class ListTest { + + @Test + public void objectMethods() { + + int max = 5; + List list = List.range(1, max); + + assertTrue(list.equals(list)); + assertTrue(list.equals(List.range(1, max))); + + assertFalse(list.equals(List.single(1))); + assertFalse(list.equals(true)); + assertFalse(list.equals(null)); + + + + assertTrue(List.list(1, 2).toString().equals("List(1,2)")); + + } + + @Test + public void integration() { + java.util.List ul = Arrays.asList(1, 2, 3); + List dl = List.iterableList(ul); + assertTrue(ul.equals(dl.toJavaList())); + + } + + @Test + public void convertToString() { + final int n = 10000; + final StringBuilder expected = new StringBuilder("List("); + for (int i = 0; i < n; i++) { + expected.append(i); + if (i < n - 1) { + expected.append(','); + } + } + expected.append(')'); + assertEquals(expected.toString(), List.range(0, n).toString()); + } + + @Test + public void partition() { + P2, List> p = List.range(1, 5).partition(i -> i % 2 == 0); + Equal> e = Equal.listEqual(Equal.intEqual); + assertTrue(e.eq(p._1(), List.list(2, 4))); + assertTrue(e.eq(p._2(), List.list(1, 3))); + } + + @Test + public void intersperseOverflow() { + // should not overflow + int n = 100000; + List list = List.replicate(n, 1).intersperse(2); + String s = list.toString(); + } + + @Test + public void listReduce() { + String list = List.range(1, 11).uncons((a, la) -> List.cons(a, la).toString(), ""); + String expected = List.range(1, 11).toString(); + assertThat(expected, equalTo(list)); + } + + @Test + 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).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 new file mode 100644 index 00000000..eee5752d --- /dev/null +++ b/core/src/test/java/fj/data/List_Traverse_Tests.java @@ -0,0 +1,44 @@ +package fj.data; + +import fj.F; +import org.junit.Assert; +import org.junit.Test; + +import static fj.data.List.list; +import static fj.data.Option.some; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class List_Traverse_Tests { + + @Test + public void shouldTraverseListWithGivenFunction(){ + List strings = list("some1", "some2", "some3", "not_some", " "); + F> f = s -> { + if(s.startsWith("some")) + return some(s); + else + return Option.none(); + }; + + Option> optStr = strings.traverseOption(f); + Assert.assertEquals("optStr should be none", Option.none(), optStr); + } + + @Test + public void shouldTraverseListWithGivenFunction2(){ + List strings = list("some1", "some2", "some3"); + F> f = s -> { + if(s.startsWith("some")) + return some(s); + else + return Option.none(); + }; + + Option> optStr = strings.traverseOption(f); + Assert.assertEquals("optStr should be some", optStr.isSome(), true); + assertThat(optStr.some(), is(List.list("some1", "some2", "some3"))); + } + +} diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java new file mode 100644 index 00000000..e03d7aae --- /dev/null +++ b/core/src/test/java/fj/data/OptionTest.java @@ -0,0 +1,433 @@ +package fj.data; + +import fj.P; +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.*; +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() { + int max = 4; + assertTrue(some(1).equals(some(1))); + assertTrue(some(List.range(1, max)).equals(some(List.range(1, max)))); + } + + @Test + 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)); + 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 new file mode 100644 index 00000000..8a78ab31 --- /dev/null +++ b/core/src/test/java/fj/data/SeqTest.java @@ -0,0 +1,290 @@ +package fj.data; + +import fj.P2; +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.Semigroup.listSemigroup; +import static fj.data.Either.*; +import static fj.data.List.arrayList; +import static fj.data.Option.*; +import static fj.data.Seq.sequenceEither; +import static fj.data.Seq.sequenceEitherLeft; +import static fj.data.Seq.sequenceEitherRight; +import static fj.data.Seq.sequenceF; +import static fj.data.Seq.sequenceIO; +import static fj.data.Seq.sequenceList; +import static fj.data.Seq.sequenceOption; +import static fj.data.Seq.sequenceP1; +import static fj.data.Seq.sequenceSeq; +import static fj.data.Seq.sequenceSet; +import static fj.data.Seq.sequenceStream; +import static fj.data.Seq.sequenceTrampoline; +import static fj.data.Seq.sequenceValidation; +import static fj.data.Seq.*; +import static fj.data.Validation.fail; +import static fj.data.Validation.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class SeqTest { + + @Test + public void objectMethods() { + Seq s1 = Seq.seq(1, 2, 3); + Seq s2 = Seq.seq(1, 2, 3); + assertTrue(s1.toString().equals("Seq(1,2,3)")); + assertTrue(s1.equals(s2)); + assertFalse(s1 == s2); + + } + + @Test + public void convertToString() { + final int n = 10000; + final StringBuilder expected = new StringBuilder("Seq("); + for (int i = 0; i < n; i++) { + expected.append(i); + if (i < n - 1) { + expected.append(','); + } + } + 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 new file mode 100644 index 00000000..e30d166a --- /dev/null +++ b/core/src/test/java/fj/data/SetTest.java @@ -0,0 +1,39 @@ +package fj.data; + +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.hamcrest.MatcherAssert.assertThat; + +public class SetTest { + + @Test + public void toStream() { + Set s = Set.set(intOrd, 1, 2, 3); + assertThat(s.toStream(), equalTo(Stream.stream(1, 2, 3))); + } + + @Test + public void testString() { + Set s = Set.set(intOrd, 1, 2, 3); + 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 new file mode 100644 index 00000000..479efc38 --- /dev/null +++ b/core/src/test/java/fj/data/StateTest.java @@ -0,0 +1,45 @@ +package fj.data; + +import org.junit.Test; + +import static fj.P.p; +import static org.junit.Assert.assertEquals; + +public class StateTest { + + @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 new file mode 100644 index 00000000..febd4567 --- /dev/null +++ b/core/src/test/java/fj/data/StreamTest.java @@ -0,0 +1,340 @@ +package fj.data; + +import fj.Equal; +import fj.P2; +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; +import java.util.ConcurrentModificationException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.Semigroup.listSemigroup; +import static fj.data.Either.left; +import static fj.data.Either.right; +import static fj.data.List.arrayList; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.Seq.arraySeq; +import static fj.data.Seq.empty; +import static fj.data.Stream.sequenceEitherLeft; +import static fj.data.Stream.sequenceEitherRight; +import static fj.data.Stream.sequenceF; +import static fj.data.Stream.sequenceIO; +import static fj.data.Stream.sequenceList; +import static fj.data.Stream.sequenceOption; +import static fj.data.Stream.sequenceP1; +import static fj.data.Stream.sequenceSeq; +import static fj.data.Stream.sequenceSet; +import static fj.data.Stream.sequenceStream; +import static fj.data.Stream.sequenceTrampoline; +import static fj.data.Stream.sequenceValidation; +import static fj.data.Stream.*; +import static fj.data.Validation.*; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; + +public class StreamTest { + + @Test + public void infiniteStream() { + Stream s = Stream.forever(Enumerator.intEnumerator, 0).bind(Stream::single); + assertThat(List.range(0, 5), is(s.take(5).toList())); + } + + @Test + public void testToString() { + Stream range = Stream.range(1); + assertThat(range.toString(), is(equalTo("Cons(1, ?)"))); + } + + /** + * This test demonstrates the known problem of creating streams from mutable structures. + * + * Some of the ways streams created in this way can fail is: + * - weak stream references getting garbage collected + * - underlying mutable data structure changes + * - iterator gets updated (e.g. iterator used to create 2 different streams). + */ + @Test(expected = ConcurrentModificationException.class) + public void iterableStreamWithStructureUpdate() { + java.util.List list = List.list(1, 2, 3).toJavaList(); + Stream s1 = Stream.iterableStream(list); + int x = s1.head(); + list.remove(1); + Stream s2 = s1.tail()._1(); + 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/TestRngState.java b/core/src/test/java/fj/data/TestRngState.java deleted file mode 100644 index 4a6e77fe..00000000 --- a/core/src/test/java/fj/data/TestRngState.java +++ /dev/null @@ -1,71 +0,0 @@ -package fj.data; - -import fj.*; -import org.junit.Assert; -import org.junit.Test; - -import static fj.data.Option.some; -import static fj.data.Stream.unfold; - -/** - * Created by mperry on 4/08/2014. - */ -public class TestRngState { - - static String expected1 = "<4,4,2,2,2,5,3,3,1,5>"; - static int size = 10; - - static Rng defaultRng() { - return new LcgRng(1); - } - - static P2 num(Rng r) { - return r.range(1, 5); - } - - static State defaultState() { - return State.unit(s -> num(s)); - } - - static F, State> nextState() { - return s -> s.mapState(p2 -> num(p2._1())); - } - - static P2 num(Rng r, int x) { - return r.range(x, x + 1); - } - - @Test - public void testUnfold() { - Stream s = unfold(r -> some(num(r).swap()), defaultRng()); - Assert.assertTrue(s.take(size).toList().toString().equals(expected1)); - } - - @Test - public void testTransitions() { - P2>, State> p = List.replicate(size, nextState()).foldLeft( - (P2>, State> p2, F, State> f) -> { - State s = f.f(p2._2()); - return P.p(p2._1().snoc(p2._2()), s); - } - , P.p(List.nil(), defaultState()) - ); - List ints = p._1().map(s -> s.eval(defaultRng())); - Assert.assertTrue(ints.toString().equals(expected1)); - } - - @Test - public void testSequence() { - List list = State.sequence(List.replicate(size, defaultState())).eval(defaultRng()); - Assert.assertTrue(list.toString().equals(expected1)); - } - - @Test - public void testTraverse() { - List list = State.traverse(List.range(1, 10), a -> (State.unit((Rng s) -> num(s, a)))).eval(defaultRng()); -// System.out.println(list.toString()); - String expected = "<1,2,3,5,6,7,7,9,10>"; - Assert.assertTrue(list.toString().equals(expected)); - } - -} diff --git a/core/src/test/java/fj/data/TestTreeMap.java b/core/src/test/java/fj/data/TestTreeMap.java deleted file mode 100644 index d4dcf5ba..00000000 --- a/core/src/test/java/fj/data/TestTreeMap.java +++ /dev/null @@ -1,26 +0,0 @@ -package fj.data; - -import fj.Ord; -import fj.P2; -import org.junit.Test; - -import java.util.ArrayList; - -/** - * Created by MarkPerry on 2/06/2014. - */ -public class TestTreeMap { - - @Test - public void testLargeInserts() { - // check that inserting a large number of items performs ok - // taken from https://code.google.com/p/functionaljava/issues/detail?id=31 and - // https://github.com/functionaljava/functionaljava/pull/13/files - final int n = 10000; - TreeMap m = TreeMap.empty(Ord.intOrd); - for (int i = 0; i < n; i++) { - m = m.set(i, "abc " + i); - } - } - -} diff --git a/core/src/test/java/fj/data/TreeMapTest.java b/core/src/test/java/fj/data/TreeMapTest.java new file mode 100644 index 00000000..051f0773 --- /dev/null +++ b/core/src/test/java/fj/data/TreeMapTest.java @@ -0,0 +1,120 @@ +package fj.data; + +import java.util.Map; + +import fj.Equal; +import fj.Ord; +import fj.P3; +import fj.Show; +import fj.P2; + +import org.junit.Test; + +import static fj.P.p; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.TreeMap.iterableTreeMap; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +public class TreeMapTest { + + @Test + public void split() { + // do the split + int pivot = 4; + int max = 5; + List l = List.range(1, max + 1); + TreeMap m2 = iterableTreeMap(Ord.intOrd, l.zip(l.map(i -> i.toString()))); + P3, Option, Set> p = m2.split(Ord.stringOrd, pivot); + + // print debug info + Show> st = Show.treeMapShow(Show.intShow, Show.stringShow); + Show> ss = Show.setShow(Show.stringShow); + Show> so = Show.optionShow(Show.stringShow); + Show, Option, Set>> sp3 = Show.p3Show(ss, so, ss); + + // assert equals + Equal> seq = Equal.setEqual(Equal.stringEqual); + Set left = toSetString(List.range(1, pivot)); + Set right = toSetString(List.range(pivot + 1, max + 1)); + P3, Option, Set> expected = p(left, some(Integer.toString(pivot)), right); + assertTrue(Equal.p3Equal(seq, Equal.optionEqual(Equal.stringEqual), seq).eq(p, expected)); + } + + private static Set toSetString(List list) { + return Set.iterableSet(Ord.stringOrd, list.map(i -> i.toString())); + } + + @Test + public void splitLookup() { + // do the split + int pivot = 4; + int max = 5; + List l = List.range(1, max + 1); + TreeMap m2 = iterableTreeMap(Ord.intOrd, l.zip(l.map(i -> i.toString()))); + P3, Option, TreeMap> p3 = m2.splitLookup(pivot); + + // create expected output + List leftList = List.range(1, pivot); + TreeMap leftMap = iterableTreeMap(Ord.intOrd, leftList.zip(leftList.map(i -> i.toString()))); + List rightList = List.range(pivot + 1, max + 1); + TreeMap rightMap = iterableTreeMap(Ord.intOrd, rightList.zip(rightList.map(i -> i.toString()))); + + // do the assert + Equal> tme = Equal.treeMapEqual(Equal.intEqual, Equal.stringEqual); + Equal, Option, TreeMap>> eq = Equal.p3Equal(tme, Equal.optionEqual(Equal.stringEqual), tme); + assertTrue(eq.eq(p3, p(leftMap, some(Integer.toString(pivot)), rightMap))); + } + + @Test + public void toMutableMap() { + int max = 5; + List> l = List.range(1, max + 1).map(n -> List.single(n)); + TreeMap, String> m2 = iterableTreeMap(Ord.listOrd(Ord.intOrd), l.zip(l.map(i -> i.toString()))); + Map, String> mm = m2.toMutableMap(); + assertEquals(m2.keys(), List.iterableList(mm.keySet())); + } + + + @Test + public void testLargeInserts() { + // check that inserting a large number of items performs ok + // taken from https://code.google.com/p/functionaljava/issues/detail?id=31 and + // https://github.com/functionaljava/functionaljava/pull/13/files + final int n = 10000; + TreeMap m = TreeMap.empty(Ord.intOrd); + for (int i = 0; i < n; i++) { + m = m.set(i, "abc " + i); + } + } + + @Test + public void testString() { + TreeMap t = TreeMap.treeMap(Ord.intOrd, p(1, "a"), p(2, "b"), p(3, "c")); + TreeMap t2 = TreeMap.treeMap(Ord.intOrd, p(3, "c"), p(2, "b"), p(1, "a")); + Stream> s = Stream.stream(p(1, "a"), p(2, "b"), p(3, "c")); + assertThat(t.toStream(), equalTo(s)); + assertThat(t2.toStream(), equalTo(s)); + } + + @Test + public void minKey() { + TreeMap t1 = TreeMap.empty(Ord.intOrd); + assertThat(t1.minKey(), equalTo(none())); + TreeMap t2 = t1.set(1, 2).set(2, 4).set(10, 20).set(5, 10).set(0, 100); + assertThat(t2.minKey(), equalTo(some(0))); + assertThat(t2.delete(0).minKey(), equalTo(some(1))); + } + + @Test + public void emptyHashCode() { + // Hash code of tree map should not throw NullPointerException + // see https://github.com/functionaljava/functionaljava/issues/187 + int i = TreeMap.empty(Ord.stringOrd).hashCode(); + assertTrue(true); + } + +} diff --git a/core/src/test/java/fj/data/TreeTest.java b/core/src/test/java/fj/data/TreeTest.java new file mode 100644 index 00000000..a6028f37 --- /dev/null +++ b/core/src/test/java/fj/data/TreeTest.java @@ -0,0 +1,48 @@ +package fj.data; + +import org.junit.Test; + +import static fj.data.Tree.leaf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +public class TreeTest { + + @Test + public void emptyLength() { + Tree t = leaf(1); + assertThat(t.length(), equalTo(1)); + } + + @Test + public void shallowStreamLength() { + Tree t2 = Tree.node(3, Stream.stream(leaf(4), leaf(5))); + assertThat(t2.length(), equalTo(3)); + } + + @Test + public void deepStreamLength() { + Tree t3 = Tree.node(4, Stream.stream(leaf(5), Tree.node(6, Stream.stream(leaf(7), leaf(8))))); + assertThat(t3.length(), equalTo(5)); + } + + + @Test + public void singleIsLeft() { + Tree t = leaf(1); + assertThat(t.isLeaf(), equalTo(true)); + } + + @Test + public void shallowStreamIsLeaf() { + Tree t2 = Tree.node(3, Stream.stream(leaf(4), leaf(5))); + assertThat(t2.isLeaf(), equalTo(false)); + } + + @Test + public void deepStreamIsLeaf() { + Tree t3 = Tree.node(4, Stream.stream(leaf(5), Tree.node(6, Stream.stream(leaf(7), leaf(8))))); + assertThat(t3.isLeaf(), equalTo(false)); + } + +} 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 new file mode 100644 index 00000000..1fab0a7b --- /dev/null +++ b/core/src/test/java/fj/data/UnitTest.java @@ -0,0 +1,16 @@ +package fj.data; + +import fj.Unit; +import org.junit.Assert; +import org.junit.Test; + +public class UnitTest { + + @Test + public void objectMethods() { + Assert.assertTrue(Unit.unit().equals(Unit.unit())); + Assert.assertFalse(Unit.unit().equals(3)); + Assert.assertTrue(Unit.unit().toString().equals("unit")); + } + +} 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/test/TestNull.java b/core/src/test/java/fj/data/test/TestNull.java deleted file mode 100644 index 6f7795aa..00000000 --- a/core/src/test/java/fj/data/test/TestNull.java +++ /dev/null @@ -1,33 +0,0 @@ -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); - }); - CheckResult.summary.println(p.check()); - } - -} 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 new file mode 100644 index 00000000..53b74e52 --- /dev/null +++ b/core/src/test/java/fj/function/DoublesTest.java @@ -0,0 +1,64 @@ +package fj.function; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + + +import fj.F; +import org.junit.Test; + +import java.lang.reflect.Constructor; +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; + +public class DoublesTest { + + @Test + public void testSum() { + assertThat(Doubles.sum(list(3.0, 4.0, 5.0)), is(12.0)); + } + + @Test + public void testProduct() { + assertThat(Doubles.product(list(3.0, 4.0, 5.0)), is(60.0)); + } + + @Test + public void testAdd() { + assertThat(Doubles.add.f(10.0).f(20.0), is(30.0)); + } + + @Test + public void testMultiply() { + assertThat(Doubles.multiply.f(3.0).f(5.0), is(15.0)); + } + + @Test + public void testAbs() { + assertThat(Doubles.abs.f(-5.0), is(5.0)); + } + + @Test + public void testFromString() { + assertThat(Doubles.fromString().f("-123.45").some(), is(-123.45)); + } + + @Test + public void testCannotInstantiate() throws NoSuchMethodException, InvocationTargetException, + IllegalAccessException, InstantiationException { + Constructor constructor = Doubles.class.getDeclaredConstructor(new Class[0]); + constructor.setAccessible(true); + try { + constructor.newInstance(new Object[0]); + fail("expected InvocationTargetException"); + } catch (InvocationTargetException ite) { + assertTrue(ite.getCause() instanceof UnsupportedOperationException); + } + } + +} 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 df833911..87bf7a5c 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -1,17 +1,19 @@ apply plugin: 'application' -apply plugin: 'retrolambda' mainClassName = "fj.demo.euler.Problem2" +archivesBaseName = "${project.projectName}-${project.name}" -retrolambda { - jdk System.getenv("JAVA8_HOME") - oldJdk System.getenv("JAVA7_HOME") - javaVersion JavaVersion.VERSION_1_7 +dependencies { + api project(":core") + api project(":quickcheck") + testImplementation junitCompile + testImplementation junitRuntime } -dependencies { - compile project(":core") - testCompile dependencyJunit +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 bbbdcd95..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; @@ -9,11 +8,7 @@ public final class Array_bind { public static void main(final String[] args) { final Array a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42); - final Array b = a.bind(new F>() { - public Array f(final Integer i) { - return array(500, i); - } - }); + final Array b = a.bind(i -> array(500, i)); arrayShow(intShow).println(b); // {500,97,500,44,500,67,500,3,500,22,500,90,500,1,500,77,500,98,500,1078,500,6,500,64,500,6,500,79,500,42} } diff --git a/demo/src/main/java/fj/demo/Array_exists.java b/demo/src/main/java/fj/demo/Array_exists.java index 0089b475..54641239 100644 --- a/demo/src/main/java/fj/demo/Array_exists.java +++ b/demo/src/main/java/fj/demo/Array_exists.java @@ -1,20 +1,14 @@ package fj.demo; -import fj.F; import fj.data.Array; import static fj.data.Array.array; import static fj.data.List.fromString; import static fj.function.Characters.isLowerCase; public final class Array_exists { - public static void main(final String[] args) { - final Array a = array("Hello", "There", "what", "DAY", "iS", "iT"); - final boolean b = a.exists(new F() { - public Boolean f(final String s) { - return fromString(s).forall(isLowerCase); - } - }); - final boolean b2 = a.exists(s -> fromString(s).forall(isLowerCase)); - System.out.println(b); // true ("what" provides the only example; try removing it) - } + public static void main(final String[] args) { + final Array a = array("Hello", "There", "what", "DAY", "iS", "iT"); + final boolean b = a.exists(s -> fromString(s).forall(isLowerCase)); + System.out.println(b); // true ("what" is the only value that qualifies; try removing it) + } } diff --git a/demo/src/main/java/fj/demo/Array_filter.java b/demo/src/main/java/fj/demo/Array_filter.java index 9b5a9330..cad6d393 100644 --- a/demo/src/main/java/fj/demo/Array_filter.java +++ b/demo/src/main/java/fj/demo/Array_filter.java @@ -7,10 +7,10 @@ import static fj.function.Integers.even; public final class Array_filter { - public static void main(final String[] args) { - final Array a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42); - final Array b = a.filter(even); - final Array c = a.filter(i -> i % 2 == 0); - arrayShow(intShow).println(b); // {44,22,90,98,1078,6,64,6,42} - } + public static void main(final String[] args) { + final Array a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42); + final Array b = a.filter(even); + final Array c = a.filter(i -> i % 2 == 0); + arrayShow(intShow).println(b); // {44,22,90,98,1078,6,64,6,42} + } } diff --git a/demo/src/main/java/fj/demo/Array_foldLeft.java b/demo/src/main/java/fj/demo/Array_foldLeft.java index 1082a07b..f7f1ce58 100644 --- a/demo/src/main/java/fj/demo/Array_foldLeft.java +++ b/demo/src/main/java/fj/demo/Array_foldLeft.java @@ -1,14 +1,19 @@ package fj.demo; +import fj.F; import fj.data.Array; import static fj.data.Array.array; import static fj.function.Integers.add; public final class Array_foldLeft { - public static void main(final String[] args) { - final Array a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42); - final int b = a.foldLeft(add, 0); - final int c = a.foldLeft(i -> (j -> i + j), 0); - System.out.println(b); // 1774 - } + public static void main(final String[] args) { + final Array a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42); + final int b = a.foldLeft(add, 0); + + // 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; + final int c = a.foldLeft(add2, 0); + System.out.println(b); // 1774 + } } diff --git a/demo/src/main/java/fj/demo/Array_forall.java b/demo/src/main/java/fj/demo/Array_forall.java index c9888823..49e971d7 100644 --- a/demo/src/main/java/fj/demo/Array_forall.java +++ b/demo/src/main/java/fj/demo/Array_forall.java @@ -1,20 +1,14 @@ package fj.demo; -import fj.F; import fj.data.Array; import static fj.data.Array.array; import static fj.data.List.fromString; import static fj.function.Characters.isLowerCase; public final class Array_forall { - public static void main(final String[] args) { - final Array a = array("hello", "There", "what", "day", "is", "it"); - final boolean b = a.forall(new F() { - public Boolean f(final String s) { - return fromString(s).forall(isLowerCase); - } - }); - final boolean b2 = a.forall(s -> fromString(s).forall(isLowerCase)); - System.out.println(b); // false ("There" is a counter-example; try removing it) - } + public static void main(final String[] args) { + final Array a = array("hello", "There", "what", "day", "is", "it"); + final boolean b = a.forall(s -> fromString(s).forall(isLowerCase)); + System.out.println(b); // false ("There" is a counter-example; try removing it) + } } diff --git a/demo/src/main/java/fj/demo/Array_map.java b/demo/src/main/java/fj/demo/Array_map.java index cc638c5c..072610cf 100644 --- a/demo/src/main/java/fj/demo/Array_map.java +++ b/demo/src/main/java/fj/demo/Array_map.java @@ -1,7 +1,6 @@ package fj.demo; import fj.data.Array; - import static fj.data.Array.array; import static fj.function.Integers.add; import static fj.Show.arrayShow; diff --git a/demo/src/main/java/fj/demo/ChequeWrite.java b/demo/src/main/java/fj/demo/ChequeWrite.java index e95950f8..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,11 +24,7 @@ public final class ChequeWrite { private ChequeWrite() {} static List toZero(final int from) { - return unfold(new F>>() { - public Option> f(final Integer i) { - return 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) { @@ -65,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' @@ -80,12 +75,9 @@ static List show(final List cs) { static List, Integer>> split(final List as) { final int len = as.length(); - final List> ds = as.zip(toZero(len - 1)).foldRight(new F2, List>, List>>() { - public List> f(final P2 ki, final List> z) { - return ki._2() % 3 == 0 ? z.conss(single(ki._1())) : z.tail().conss(z.head().cons(ki._1())); - } - }, List.>nil()); - + 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() + ); return ds.zip(toZero(len / 3 + signum(len % 3) - 1)); } @@ -103,40 +95,33 @@ static boolean and(final List cs) { } static boolean existsNotZero(final List cs) { - return cs.exists(new F() { - public Boolean f(final Character c) { - return c != '0'; - } - }); + return cs.exists(c -> c != '0'); } static boolean eq(final List a, final List b) { return listEqual(charEqual).eq(a, b); } - static final F, List> dollars = new F, List>() { - public List f(final List cs) { + static final F, List> dollars = cs -> { 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)); } }); @@ -144,25 +129,19 @@ public void f(final P2, Integer> z) { return fromString(" ").intercalate(x.toList()); } - } + }; - static final F, List> cents = new F, List>() { - public List f(final List a) { + static final F, List> cents = a -> { final int n = a.length(); return n == 0 ? fromString("zero cents") : show(list(a.head(), n == 1 ? '0' : a.tail().head())) .append(fromString(eq(a, list('0', '1')) ? " cent" : " cents")); - } }; public static List write(final List cs) { - final F, List> dropNonDigit = new F, List>() { - public List f(final List cs) { - return cs.filter(Characters.isDigit); - } - }; + final F, List> dropNonDigit = cs2 -> cs2.filter(Characters.isDigit); final P2, List> x = cs.dropWhile(charEqual.eq('0')).breakk(charEqual.eq('.')).map1(dropNonDigit).map1(dollars).map2(dropNonDigit) .map2(List.take().f(2)).map2(cents); diff --git a/demo/src/main/java/fj/demo/Comonad_example.java b/demo/src/main/java/fj/demo/Comonad_example.java index a52b8957..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,14 +21,13 @@ 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(new F, Stream>>() { - public Stream> f(final Zipper zp) { - return perms(zp.lefts().reverse().append(zp.rights())) - .map(F1Functions.o(Stream.cons().f(zp.focus()), P.>p1())); - } - }).toStream()); + r = join(z.cobind(zp -> + perms(zp.lefts().reverse().append(zp.rights())).map( + 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 new file mode 100644 index 00000000..aad8e41b --- /dev/null +++ b/demo/src/main/java/fj/demo/IODemo.java @@ -0,0 +1,55 @@ +package fj.demo; + +import fj.F; +import fj.data.IOFunctions; +import fj.data.LazyString; + +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; + +public class IODemo { + + public static void main(String[] args) { + IODemo d = new IODemo(); +// d.readFirstShortLine(); +// d.readFirstLine(); +// d.simpleInteract(); + d.getContents(); + } + + /** + * Reads from standard input until the line length is less than three + * and prints that last line. + */ + 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 final void readFirstLine() { + F f = lines_().andThen(unlines_()); + runSafe(interact(f)); + } + + /** + * Demonstrate use of interact, just echoing the lazy string. Reading lines is done + * lazily, so just the first line is read. + */ + public final void simpleInteract() { + runSafe(interact(s -> s)); + } + + /** + * Demonstrate that getContents returns a lazy string. + */ + 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 new file mode 100644 index 00000000..b415e19c --- /dev/null +++ b/demo/src/main/java/fj/demo/List_groupBy.java @@ -0,0 +1,42 @@ +package fj.demo; + +import fj.Function; +import fj.Ord; +import fj.data.List; +import fj.data.TreeMap; + +import static fj.data.List.list; + +public final class List_groupBy { + + public static void main(final String... args) { + keyDemo(); + keyValueDemo(); + keyValueAccDemo(); + } + + 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, Ord.intOrd); + + 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, Ord.intOrd); + + 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.intOrd); + + 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 83f89ead..a07f0015 100644 --- a/demo/src/main/java/fj/demo/List_map.java +++ b/demo/src/main/java/fj/demo/List_map.java @@ -1,7 +1,6 @@ package fj.demo; import fj.data.List; - import static fj.data.List.list; import static fj.function.Integers.add; import static fj.Show.intShow; @@ -10,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 4a478a22..edf3dd83 100644 --- a/demo/src/main/java/fj/demo/Option_bind.java +++ b/demo/src/main/java/fj/demo/Option_bind.java @@ -2,38 +2,28 @@ import fj.F; import fj.data.Option; -import static fj.data.Option.none; -import static fj.data.Option.some; import static fj.Show.intShow; import static fj.Show.optionShow; +import static fj.data.Option.none; +import static fj.data.Option.some; public final class Option_bind { - public static void main(final String[] args) { - final Option o1 = some(7); - final Option o2 = some(8); - final Option o3 = none(); - final Option p1 = o1.bind(new F>() { - public Option f(final Integer i) { - return i % 2 == 0 ? some(i * 3) : Option.none(); - } - }); - final Option p2 = o2.bind(new F>() { - public Option f(final Integer i) { - return i % 2 == 0 ? some(i * 3) : Option.none(); - } - }); - final Option p3 = o3.bind(new F>() { - public Option f(final Integer i) { - return i % 2 == 0 ? some(i * 3) : Option.none(); - } - }); + public static void main(final String[] args) { + final Option o1 = some(7); + final Option o2 = some(8); + final Option o3 = none(); + + F> f = i -> i % 2 == 0 ? some(i * 3) : none(); + final Option o4 = o1.bind(f); + final Option o5 = o2.bind(f); + final Option o6 = o3.bind(f); - final Option p4 = o1.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); - final Option p5 = o2.bind(i -> i % 2 == 0 ? some(i * 3) : Option.none()); - final Option p6 = 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(p1); // None - optionShow(intShow).println(p2); // Some(24) - optionShow(intShow).println(p3); // None - } + optionShow(intShow).println(o4); // None + optionShow(intShow).println(o5); // Some(24) + optionShow(intShow).println(o6); // None + } } diff --git a/demo/src/main/java/fj/demo/Option_filter.java b/demo/src/main/java/fj/demo/Option_filter.java index ee7fa1ca..aac3538f 100644 --- a/demo/src/main/java/fj/demo/Option_filter.java +++ b/demo/src/main/java/fj/demo/Option_filter.java @@ -1,27 +1,30 @@ package fj.demo; +import fj.F; import fj.data.Option; +import static fj.Show.intShow; +import static fj.Show.optionShow; import static fj.data.Option.none; import static fj.data.Option.some; import static fj.function.Integers.even; -import static fj.Show.intShow; -import static fj.Show.optionShow; public final class Option_filter { - public static void main(final String[] args) { - final Option o1 = some(7); - final Option o2 = none(); - final Option o3 = some(8); - final Option p1 = o1.filter(even); - final Option p2 = o2.filter(even); - final Option p3 = o3.filter(even); + public static void main(final String[] args) { + final Option o1 = some(7); + final Option o2 = none(); + final Option o3 = some(8); + + final Option o4 = o1.filter(even); + final Option o5 = o2.filter(even); + final Option o6 = o3.filter(even); - final Option p4 = o1.filter(i -> i % 2 == 0); - final Option p5 = o1.filter(i -> i % 2 == 0); - final Option p6 = o1.filter(i -> i % 2 == 0); + F f = i -> i % 2 == 0; + final Option o7 = o4.filter(f); + final Option o8 = o5.filter(f); + final Option o9 = o6.filter(i -> i % 2 == 0); - optionShow(intShow).println(p1); // None - optionShow(intShow).println(p2); // None - optionShow(intShow).println(p3); // Some(8) - } + optionShow(intShow).println(o7); // None + optionShow(intShow).println(o8); // None + optionShow(intShow).println(o9); // Some(8) + } } diff --git a/demo/src/main/java/fj/demo/Option_map.java b/demo/src/main/java/fj/demo/Option_map.java index 5f947b04..fa637385 100644 --- a/demo/src/main/java/fj/demo/Option_map.java +++ b/demo/src/main/java/fj/demo/Option_map.java @@ -1,23 +1,23 @@ package fj.demo; import fj.data.Option; +import static fj.Show.intShow; +import static fj.Show.optionShow; import static fj.data.Option.none; import static fj.data.Option.some; import static fj.function.Integers.add; -import static fj.Show.intShow; -import static fj.Show.optionShow; public final class Option_map { - public static void main(final String[] args) { - final Option o1 = some(7); - final Option o2 = none(); - final Option p1 = o1.map(add.f(42)); - final Option p2 = o2.map(add.f(42)); + public static void main(final String[] args) { + final Option o1 = some(7); + final Option o2 = none(); + final Option p1 = o1.map(add.f(42)); + final Option p2 = o2.map(add.f(42)); - final Option p3 = o1.map(i -> i + 42); - final Option p4 = o2.map(i -> i + 42); + final Option p3 = o1.map(i -> i + 42); + final Option p4 = o2.map(i -> i + 42); - optionShow(intShow).println(p1); // Some(49) - optionShow(intShow).println(p2); // None - } + optionShow(intShow).println(p1); // Some(49) + optionShow(intShow).println(p2); // None + } } diff --git a/demo/src/main/java/fj/demo/Primes2.java b/demo/src/main/java/fj/demo/Primes2.java index 0a79dd75..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,11 +19,7 @@ public class Primes2 { // Finds primes in a given stream. public static Stream sieve(final Stream xs) { - return cons(xs.head(), new P1>() { - public Stream _1() { - return 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 592057ca..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; @@ -10,11 +9,7 @@ public final class Set_map { public static void main(final String[] args) { final Set a = empty(Ord.intOrd).insert(1).insert(2).insert(3).insert(4).insert(5).insert(6); - final Set b = a.map(Ord.intOrd, new F() { - public Integer f(final Integer i) { - return i / 2; // divide each integer by 2 - } - }); + final Set b = a.map(Ord.intOrd, i -> i / 2); listShow(intShow).println(b.toList()); // [3,2,1,0] } } 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/Stream_Test.java b/demo/src/main/java/fj/demo/Stream_Test.java index 47dd5937..eb22bcb3 100644 --- a/demo/src/main/java/fj/demo/Stream_Test.java +++ b/demo/src/main/java/fj/demo/Stream_Test.java @@ -1,16 +1,42 @@ package fj.demo; +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.List; +import fj.data.Stream; + import static fj.data.Enumerator.naturalEnumerator; +import static fj.data.IOFunctions.runSafe; +import static fj.data.IOFunctions.stdinReadLine; import static fj.data.Natural.natural; import static fj.data.Stream.forever; import static fj.Show.naturalShow; import static fj.Show.unlineShow; +import static java.lang.System.out; -/** - * Produces natural numbers forever. - */ public class Stream_Test { + public static void main(final String[] args) { + sequenceWhile(); +// foreverNaturals(); + } + + /** + * Produces natural numbers forever. + */ + static void foreverNaturals() { unlineShow(naturalShow).println(forever(naturalEnumerator, natural(3).some(), 2)); } + + /** + * Reads lines from standard input until a line's length is less than two or three lines + * have been read. + */ + static void sequenceWhile() { + Stream> s = Stream.repeat(stdinReadLine()); + IO> io = IOFunctions.sequenceWhile(s, s2 -> s2.length() > 1); + List list = runSafe(io).take(3).toList(); + out.println("list: " + list + " size: " + list.length()); + } + } 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 72e68391..38a62caa 100644 --- a/demo/src/main/java/fj/demo/concurrent/MapReduce.java +++ b/demo/src/main/java/fj/demo/concurrent/MapReduce.java @@ -1,9 +1,6 @@ package fj.demo.concurrent; -import fj.F; -import fj.F1Functions; -import fj.P1; -import fj.Unit; +import fj.*; import fj.control.parallel.ParModule; import static fj.control.parallel.ParModule.parModule; import fj.control.parallel.Promise; @@ -31,25 +28,22 @@ public class MapReduce { // Count words of documents in parallel public static Promise countWords(final List> documents, final ParModule m) { - return m.parFoldMap(documents, new F, Long>() { - public Long f(final Stream document) { - return (long) fromStream(document).words().length(); - } - }, longAdditionMonoid); + return m.parFoldMap(documents, + document -> (long) fromStream(document).words().length(), longAdditionMonoid + ); } // 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(new F() { - public BufferedReader f(final String 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 { @@ -58,11 +52,7 @@ public Stream f(final BufferedReader reader) { throw new Error(e); } if (s.isSome()) - return fromString(s.some()).append(new P1>() { - public Stream _1() { - return f(reader); - } - }); + return fromString(s.some()).append(() -> f(reader)); else { try { reader.close(); @@ -75,7 +65,7 @@ public Stream _1() { })); 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 277ca9dc..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,42 +22,27 @@ 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 = new F() { - @Override - public Integer f(Integer a) { - return a.intValue() + 1; - } - }; + private static final F addOne = a -> a.intValue() + 1; private static Map update(Map map, K key, F valueFunction, V initialValue) { @@ -59,38 +53,29 @@ private static Map update(Map map, K key, F valueFuncti map.put(key, valueFunction.f(value)); return map; } - - private static final F> fileNameToWordsAndCountsWithCharChunkIteratee = new F>() { - @Override - public Map f(final String fileName) { + + 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 = new F>() { - @Override - public Map f(final String fileName) { + + 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 = new F>() { - @Override - public Map f(final String fileName) { + + 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); } - } }; /** An iteratee that consumes char chunks and calculates word counts */ @@ -102,55 +87,40 @@ public static final IterV> wordCountsFromCharCh @Override public F, IterV>> f(final P2> acc) { final P1>> empty = - new P1>>() { - @Override - public IterV> _1() { - return IterV.cont(step.f(acc)); - } - }; + P.lazy(() -> IterV.cont(step.f(acc))); final P1>>> el = new P1>>>() { @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))); }; } }; final P1>> eof = - new P1>>() { - @Override - public IterV> _1() { + 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 new F, IterV>>() { - @Override - public IterV> f(final Input s) { - return s.apply(empty, el, eof); - } - }; + return IterV.done(acc._2(), Input.eof()); + }); + + return s -> s.apply(empty, el, eof); } }; return IterV.cont(step.f(P.p(new StringBuilder(), (Map)new HashMap()))); @@ -164,57 +134,35 @@ public static final IterV> wordCountsFromCha @Override public F, IterV>> f(final P2> acc) { - final P1>> empty = - new P1>>() { - @Override - public IterV> _1() { - return IterV.cont(step.f(acc)); - } - }; + final P1>> empty = P.lazy(() -> IterV.cont(step.f(acc))); final P1>>> el = - new P1>>>() { - @Override - public F>> _1() { - return new F>>() { - @Override - public IterV> f(final Character 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))); } - }; - } - }; - final P1>> eof = - new P1>>() { - @Override - public IterV> _1() { + 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 new F, IterV>>() { - @Override - public IterV> f(final Input s) { - return s.apply(empty, el, eof); - } - }; + ); + return s -> s.apply(empty, el, eof); } }; return IterV.cont(step.f(P.p(new StringBuilder(), (Map)new HashMap()))); @@ -229,13 +177,9 @@ public static void main(String[] args) throws IOException { final P2, Map> result = writeSampleFiles(numFiles, numSharedWords); final List fileNames = result._1(); final Map expectedWordsAndCounts = result._2(); - long avgSize = fileNames.foldLeft(new F2() { - @Override - public Long f(Long a, String file) { - return a.longValue() + new File(file).length(); - }}, 0l) / fileNames.length(); + 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(); @@ -265,7 +209,7 @@ public Long f(Long a, String file) { assertTrue(wordsAndCountsFromFiles != null); assertEquals(wordsAndCountsFromFiles.size(), numFiles + numSharedWords); assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); - + System.gc(); // get word counts sequentially / single threaded \w iteratee @@ -277,7 +221,7 @@ public Long f(Long a, String file) { assertEquals(wordsAndCountsFromFiles, expectedWordsAndCounts); System.gc(); - + // get word counts sequentially / single threaded \w iteratee start = System.currentTimeMillis(); wordsAndCountsFromFiles = getWordsAndCountsFromFilesWithIteratee(fileNames, fileNameToWordsAndCountsWithCharChunk2Iteratee); @@ -287,7 +231,7 @@ public Long f(Long a, String file) { 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."); @@ -303,21 +247,16 @@ public Long f(Long a, String file) { 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") @@ -329,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"); @@ -345,22 +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(new F2, Map, Map>() { - @Override - public Map f(Map a, Map b) { - return 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(); @@ -369,40 +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 = - new F, F, Map>>() { - @Override - public F, Map> f( - final Map a) { - return new F, Map>() { - - @Override - public Map f(final Map b) { - return 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; @@ -423,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 8f7cf5a6..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; @@ -18,13 +17,14 @@ public static void main(final String[] args) { } public static void java7() { - out.println(sum(range(0, 1000).filter(new F() { - public Boolean f(final Integer a) { return a % 3 == 0 || a % 5 == 0;} - }))); + out.println(sum(range(0, 1000).filter(a -> a % 3 == 0 || a % 5 == 0))); } public static void java8() { - out.println(Stream.range(0, 1000).filter(i -> i % 3 == 0 || i % 5 == 0).foldLeft((acc, i) -> acc + i, 0)); + // WARNING: In JDK 8, update 20 and 25 (current version) the following code causes 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. +// out.println(Stream.range(0, 1000).filter(i -> i % 3 == 0 || i % 5 == 0).foldLeft((acc, i) -> acc + i, 0)); + Integer sum = Stream.range(0, 1000).filter(i -> i % 3 == 0 || i % 5 == 0).foldLeft((acc, i) -> acc + i, 0); + out.println(sum); } } 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 c3c6dde1..e429e0e9 100644 --- a/demo/src/main/java/fj/demo/euler/Problem3.java +++ b/demo/src/main/java/fj/demo/euler/Problem3.java @@ -1,6 +1,5 @@ package fj.demo.euler; -import fj.F; import fj.P1; import static fj.data.Enumerator.naturalEnumerator; import fj.data.Natural; @@ -17,13 +16,7 @@ */ public class Problem3 { // An infinite stream of all the primes. - public static final Stream primes = cons(natural(2).some(), new P1>() { - public Stream _1() { - return forever(naturalEnumerator, natural(3).some(), 2).filter(new F() { - public Boolean f(final Natural n) {return primeFactors(n).length() == 1;} - }); - } - }); + public static final Stream primes = cons(natural(2).some(), () -> forever(naturalEnumerator, natural(3).some(), 2).filter(n -> primeFactors(n).length() == 1)); //Finds factors of a given number. public static Stream factor(final Natural n, final Natural p, final P1> ps) { @@ -37,9 +30,7 @@ public static Stream factor(final Natural n, final Natural p, final P1< else { final V2 dm = n.divmod(h); if (naturalOrd.eq(dm._2(), ZERO)) - ret = cons(h, new P1>() { - public Stream _1() {return factor(dm._1(), h, t);} - }); + ret = cons(h, () -> factor(dm._1(), h, t)); else ns = ns.tail()._1(); } } diff --git a/demo/src/main/java/fj/demo/euler/Problem4.java b/demo/src/main/java/fj/demo/euler/Problem4.java index c021d920..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; @@ -17,11 +16,9 @@ public class Problem4 { public static void main(final String[] a) { final Stream tdl = iterate(flip(subtract).f(1), 999).takeWhile(intOrd.isGreaterThan(99)); - intShow.println(tdl.tails().bind(tdl.zipWith(multiply)).filter(new F() { - public Boolean f(final Integer i) { + 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 new file mode 100644 index 00000000..641543e9 --- /dev/null +++ b/demo/src/main/java/fj/demo/optic/LensPerson.java @@ -0,0 +1,80 @@ +package fj.demo.optic; + +import fj.Equal; +import fj.data.optic.Lens; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class LensPerson { + + 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; + } + } + + static Lens personNameLens = Lens.lens(p -> p.name, s -> p -> new Person(s, p.address)); + static Lens personAddressLens = Lens.lens(p -> p.address, a -> p -> new Person(p.name, a)); + static Lens addressNumberLens = Lens.lens(a -> a.number, n -> a -> new Address(n, a.street)); + static Lens addressStreetLens = Lens.lens(a -> a.street, s -> a -> new Address(a.number, s)); + static Lens personNumberLens = personAddressLens.composeLens(addressNumberLens); + static Lens personStreetLens = personAddressLens.composeLens(addressStreetLens); + + static Equal
addressEqual = Equal.equal(a1 -> a2 -> a1.number == a2.number && a1.street.equals(a2.street)); + static Equal personEqual = Equal.equal(p1 -> p2 -> p1.name.equals(p2.name) && addressEqual.eq(p1.address, p2.address)); + + static final String oldName = "Joe"; + static final int oldNumber = 10; + static final String oldStreet = "Main St"; + static final Address oldAddress = new Address(oldNumber, oldStreet); + static final Person oldPerson = new Person(oldName, oldAddress); + + @Test + public final void get() { + assertTrue(personNameLens.get(oldPerson).equals(oldName)); + assertTrue(personNumberLens.get(oldPerson) == oldNumber); + assertTrue(personStreetLens.get(oldPerson) == oldStreet); + } + + @Test + public final void setName() { + String newName = "Bill"; + Person p = personNameLens.set(newName).f(oldPerson); + assertTrue(p.name.equals(newName)); + assertTrue(addressEqual.eq(p.address, oldPerson.address)); + } + + @Test + public final void setNumber() { + int newNumber = 20; + Person p = personNumberLens.set(newNumber).f(oldPerson); + assertTrue(p.name.equals(oldName)); + assertTrue(p.address.number == newNumber); + assertTrue(p.address.street.equals(oldStreet)); + } + + @Test + public final void setStreet() { + String newStreet = "First St"; + Person p = personStreetLens.set(newStreet).f(oldPerson); + assertTrue(p.name.equals(oldName)); + assertTrue(p.address.number == oldPerson.address.number); + assertTrue(p.address.street.equals(newStreet)); + } + +} diff --git a/demo/src/main/java/fj/demo/realworld/Chapter7.java b/demo/src/main/java/fj/demo/realworld/Chapter7.java new file mode 100644 index 00000000..bc414e4f --- /dev/null +++ b/demo/src/main/java/fj/demo/realworld/Chapter7.java @@ -0,0 +1,50 @@ +package fj.demo.realworld; + +import fj.data.IO; +import fj.data.IOFunctions; +import fj.data.LazyString; +import fj.data.Stream; + +import static fj.data.IOFunctions.*; + +/** + * Examples from Chapter 7 of Real World Haskell, http://book.realworldhaskell.org/. + * + * Currently just ch07/toupper-lazy4.hs. + */ +public class Chapter7 { + + public static void main(String[] args) { +// toUpperLazy(); +// toUpperByLine(); + toUpperInteract(); + } + + /** + * Lazy interact to upper, shows the first lazy string line. + */ + public static void toUpperLazy() { + runSafe(interact(ls -> { + Stream stream = ls.lines().map((LazyString ls2) -> ls2.eval().toUpperCase()); + return LazyString.unlines(stream.map(LazyString::str)); + })); + } + + /** + * Read each line, convert to uppercase and print on stdout, until an empty line + */ + public static void toUpperByLine() { + Stream> s1 = Stream.repeat(stdinReadLine()); + IO> io = sequenceWhile(s1, s -> s.trim().length() > 0); + runSafe(io).foreachDoEffect(s -> runSafe(stdoutPrintln(s.toUpperCase()))); + } + + /** + * Read from stdin each line, whilst each line is not empty, print + * uppercase line to stdout + */ + public static void toUpperInteract() { + 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 e1770034..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; @@ -13,11 +12,7 @@ */ public final class AdditionCommutes { public static void main(final String[] args) { - final Property p = property(arbInteger, arbInteger, new F2() { - public Property f(final Integer a, final Integer b) { - return prop(a + b == b + a); - } - }); + final Property p = property(arbInteger, arbInteger, (a, b) -> prop(a + b == b + a)); summary.println(p.check()); // OK, passed 100 tests. } } diff --git a/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java b/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java index c254b67b..d61ffeb0 100644 --- a/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java +++ b/demo/src/main/java/fj/demo/test/AdditionCommutesParallel.java @@ -1,7 +1,6 @@ package fj.demo.test; -import fj.F2; -import fj.P1; +import fj.P; import fj.control.parallel.Strategy; import static fj.test.Arbitrary.arbInteger; import static fj.test.CheckResult.summary; @@ -19,15 +18,7 @@ public final class AdditionCommutesParallel { public static void main(final String[] args) { final ExecutorService pool = Executors.newFixedThreadPool(8); final Strategy s = Strategy.executorStrategy(pool); - final Property p = propertyP(arbInteger, arbInteger, new F2>() { - public P1 f(final Integer a, final Integer b) { - return s.par(new P1() { - public Property _1() { - return prop(a + b == b + a); - } - }); - } - }); + final Property p = propertyP(arbInteger, arbInteger, (a, b) -> s.par(P.lazy(() -> prop(a + b == b + a)))); summary.println(p.check(1000000, 5000000, 0, 100)); // OK, passed 1000000 tests. pool.shutdown(); } diff --git a/demo/src/main/java/fj/demo/test/EqualsHashCode.java b/demo/src/main/java/fj/demo/test/EqualsHashCode.java index 3b0d8fbf..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,34 +54,18 @@ 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(new F() { - public Byte f(final Byte b) { - return (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(new F3() { - public String f(final Character c1, final Character c2, final Character c3) { - return 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(new F2() { - public MyClass f(final Byte b, final String s) { - return 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. - final Property p = property(arbMyClass, arbMyClass, new F2() { - public Property f(final MyClass m1, final MyClass m2) { - return bool(m1.equals(m2)).implies(m1.hashCode() == m2.hashCode()); - } - }); + final Property p = property(arbMyClass, arbMyClass, (m1, m2) -> bool(m1.equals(m2)).implies(m1.hashCode() == m2.hashCode())); // at least 100 from 10,000 should satisfy the premise (m1.equals(m2)) summary.println(p.check(100, 10000, 0, 100)); // OK, passed 100 tests (4776 discarded). } diff --git a/demo/src/main/java/fj/demo/test/IntegerOverflow.java b/demo/src/main/java/fj/demo/test/IntegerOverflow.java index c82a2102..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; @@ -16,11 +15,7 @@ public final class IntegerOverflow { public static void main(final String[] args) { final Property p = property(arbIntegerBoundaries, arbIntegerBoundaries, shrinkInteger, shrinkInteger, - new F2() { - public Property f(final Integer a, final Integer b) { - return bool(a > 0 && b > 0).implies(a + b > 0); - } - }); + (a, b) -> bool(a > 0 && b > 0).implies(a + b > 0)); summary.println(p.check()); // Falsified after 4 passed tests with arguments: [8,2147483647] } } diff --git a/demo/src/main/java/fj/demo/test/JavaLinkedList.java b/demo/src/main/java/fj/demo/test/JavaLinkedList.java index eb8b2bd8..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; @@ -17,12 +16,10 @@ public final class JavaLinkedList { public static void main(final String[] args) { final Property p = property(arbLinkedList(arbInteger), arbLinkedList(arbInteger), - new F2, LinkedList, Property>() { - public Property f(final LinkedList x, final LinkedList y) { - final LinkedList xy = new LinkedList(x); + (x, y) -> { + final LinkedList xy = new LinkedList<>(x); xy.addAll(y); return prop(xy.size() == x.size() + y.size()); - } }); summary.println(p.check()); // OK, passed 100 tests. } diff --git a/demo/src/main/java/fj/demo/test/ListFunctorLaws.java b/demo/src/main/java/fj/demo/test/ListFunctorLaws.java index edf94ed8..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,32 +37,24 @@ 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), new F, Property>() { - public Property f(final List x) { - return 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), new F3, F, List, Property>() { - public Property f(final F f, final F g, final List 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)); - } }); // identity: OK, passed 1000 tests. // 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 f5cf0e40..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,11 +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), new F2, F, Property>() { - public Property f(final Option o, final F f) { - return 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 f83b8ce8..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; @@ -52,60 +50,37 @@ public final class Reflect { @Name("Integer Addition Commutes") @Category("Passes for demo") @CheckParams(minSuccessful = 20) - final Property p1 = property(arbInteger, arbInteger, new F2() { - public Property f(final Integer a, final Integer b) { - return prop(a + b == b + a); - } - }); + final Property p1 = property(arbInteger, arbInteger, (a, b) -> prop(a + b == b + a)); @Name("Natural Integer Addition yields Natural Integer") @Category("Fails for demo") - final Property p2 = property(arbIntegerBoundaries, arbIntegerBoundaries, shrinkInteger, shrinkInteger, - new F2() { - public Property f(final Integer a, final Integer b) { - return bool(a > 0 && b > 0).implies(a + b > 0); - } - }); + final Property p2 = property(arbIntegerBoundaries, arbIntegerBoundaries, shrinkInteger, shrinkInteger, (a, b) -> bool(a > 0 && b > 0).implies(a + b > 0)); @Category("Passes for demo") - final Property p3 = property(arbStringBuilder, new F() { - public Property f(final StringBuilder sb) { - return prop(stringBuilderEqual.eq(sb, sb.reverse().reverse())); - } - }); + final Property p3 = property(arbStringBuilder, sb -> prop(stringBuilderEqual.eq(sb, sb.reverse().reverse()))); @Category("Passes for demo") - final Property p4 = property(arbCharacter, new F() { - public Property f(final Character c) { - return prop(stringBuilderEqual.eq(new StringBuilder().append(c), new StringBuilder().append(c).reverse())); - } - }); + final Property p4 = property(arbCharacter, c -> prop(stringBuilderEqual.eq(new StringBuilder().append(c), new StringBuilder().append(c).reverse()))); @Category("Passes for demo") @CheckParams(minSuccessful = 750, maxSize = 40) - final Property p5 = property(arbStringBuilder, arbStringBuilder, new F2() { - public Property f(final StringBuilder x, final StringBuilder y) { + final Property p5 = property(arbStringBuilder, arbStringBuilder, (x, y) -> { // copy the string builders before performing updates on x and y. final StringBuilder xx = new StringBuilder(x); final StringBuilder yy = new StringBuilder(y); return prop(stringBuilderEqual.eq(x.append(y).reverse(), yy.reverse().append(xx.reverse()))); - } }); @Name("Triangulation") @Category("Fails for demo") - final Property p6 = property(arbInteger, new F() { - public Property f(final Integer a) { - return prop(Triangulation.isPositive(a) == (a != 0 && !Triangulation.isNegative(a))); - } - }); + final Property p6 = property(arbInteger, a -> prop(Triangulation.isPositive(a) == (a != 0 && !Triangulation.isNegative(a)))); @NoCheck 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"); } @@ -144,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 b5155739..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; @@ -22,34 +20,20 @@ */ public final class StringBuilderReverse { public static void main(final String[] args) { - final Property p1 = property(arbStringBuilder, new F() { - public Property f(final StringBuilder sb) { - return prop(stringBuilderEqual.eq(new StringBuilder(sb), sb.reverse().reverse())); - } - }); + final Property p1 = property(arbStringBuilder, sb -> prop(stringBuilderEqual.eq(new StringBuilder(sb), sb.reverse().reverse()))); - final Property p2 = property(arbCharacter, new F() { - public Property f(final Character c) { - return prop(stringBuilderEqual.eq(new StringBuilder().append(c), new StringBuilder().append(c).reverse())); - } - }); + final Property p2 = property(arbCharacter, c -> prop(stringBuilderEqual.eq(new StringBuilder().append(c), new StringBuilder().append(c).reverse()))); - final Property p3 = property(arbStringBuilder, arbStringBuilder, new F2() { - public Property f(final StringBuilder x, final StringBuilder y) { + final Property p3 = property(arbStringBuilder, arbStringBuilder, (x, y) -> { // copy the string builders before performing updates on x and y. final StringBuilder xx = new StringBuilder(x); final StringBuilder yy = new StringBuilder(y); return prop(stringBuilderEqual.eq(x.append(y).reverse(), yy.reverse().append(xx.reverse()))); - } }); //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 cc460c95..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; @@ -23,11 +22,7 @@ static boolean isPositive(final int i) { } public static void main(final String[] args) { - final Property p = property(arbInteger, new F() { - public Property f(final Integer a) { - return prop(isPositive(a) == (a != 0 && !isNegative(a))); - } - }); + final Property p = property(arbInteger, a -> prop(isPositive(a) == (a != 0 && !isNegative(a)))); summary.println(p.check()); // Falsified after 0 passed tests with argument: 0 } } diff --git a/demo/src/test/java/fj/EmptyTest.java b/demo/src/test/java/fj/EmptyTest.java new file mode 100644 index 00000000..13675822 --- /dev/null +++ b/demo/src/test/java/fj/EmptyTest.java @@ -0,0 +1,15 @@ +package fj; + +import org.junit.Ignore; +import org.junit.Test; + +import org.junit.Assert; + +public class EmptyTest { + + @Ignore @Test + public void missing() { + Assert.fail("not implemented"); + + } +} diff --git a/etc/CONTRIBUTORS b/etc/CONTRIBUTORS index 4c4085b1..06536883 100644 --- a/etc/CONTRIBUTORS +++ b/etc/CONTRIBUTORS @@ -1,6 +1,7 @@ Tony Morris Workingmouse Pty. Ltd. tony@functionaljava.org +tmorris@tmorris.net http://tmorris.net/ -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.6 (GNU/Linux) @@ -36,6 +37,7 @@ gAAKCRCaemCth7qvrcbtAJ9j3C6lKNRB3uKcrfze66jAVQh0qACaAysOK82TcQ/2 Runar Bjarnason runar@functionaljava.org +runarorama@gmail.com -----BEGIN PGP PUBLIC KEY BLOCK----- Version: PGP Key Server 0.9.6 @@ -117,16 +119,18 @@ Workingmouse Pty. Ltd. brad@functionaljava.org http://bradclow.blogspot.com/ - Ricky Clarkson ricky@functionaljava.org +ricky.clarkson@gmail.com http://rickyclarkson.blogspot.com/ Nick Partridge nick@functionaljava.org Jason Zaugg +retronym jason@functionaljava.org +jzaugg@gmail.com Dylan Just dylan@techtangents.com @@ -134,3 +138,90 @@ dylan@techtangents.com Sam Reid reids@colorado.edu +Mark Perry +maperry78@yahoo.com.au + +Dobes Vandermeer +dobesv@gmail.com + +daneko +3a3k.web+github@gmail.com + +Amar Potghan +amarpotghan@gmail.com + +Alexey Mz +zackflame@gmail.com + +Paul Horn +knutwalker@gmail.com + +Martin Seeler +mseeler@avantgarde-labs.de + +rxxg +robbiexgibson@yahoo.com + +Piotr Bzdyl +piotr@bzdyl.net + +Steven Ruppert +blendmaster@gmail.com + +xuwei-k +6b656e6a69@gmail.com + +Andrea Richiardi +a.richiardi.work@gmail.com + +Martin Grotzke +martin.grotzke@googlemail.com + +Dan Rosen +dr@stealthy.(none) + +Grégoire Neuville +gregoire.neuville@gmail.com + +Tom Crockett +pelotom@gmail.com + +Pawel Krupinski +pawel.krupinski@youdevise.com +pawel.krupinski@gmail.com + +Marc Siegel +marc.siegel@timgroup.com + +Gary Pamparà +gpampara@gmail.com + +Kenji Yoshida +6b656e6a69@gmail.com + +etorreborre +etorreborre@yahoo.com + +Gabor Liptak +gliptak@gmail.com + +Greg Row +gregrow@users.noreply.github.com + +Jean-Baptiste Giraudeau +jb@giraudeau.info + +Zheka Kozlov +@orionll + +@mrbackend + +Brian McKenna + +Clinton Selke + +Ryan Johnson + +Charles O'Farrell + +Fabian Kramer diff --git a/etc/release-notes.md b/etc/release-notes.md deleted file mode 100644 index b0b9219a..00000000 --- a/etc/release-notes.md +++ /dev/null @@ -1,95 +0,0 @@ - -Release Notes -============= - -4.2 -=== -Release target: Aug 2014 - -* Enhancements - * Added Java 8 examples - * Added new website - * Added Option.none_() - * Gradle 2.0 support with wrapper - * Added to Strings: isNullOrEmpty, isNullOrBlank and isNotNullOrBlank - * Added Try with arity 0-8 for lambdas that throw exceptions - * Conversion from Java 8 types to Functional Java types and vice-versa - * Added monadic IO methods - * Support instantiation of IO with lambdas - * Added first class cons_ for List - * Added partial application of first parameter for F 1-8 - * Added SafeIO that encodes IOExceptions into Validations - * Added simple RNG - * Added Reader, Writer and State - * Deprecated $._ - * Added Array.scan family of methods - * Support instantiating P1 values using lambdas to P.lazy - * Added toString for Validation, P arities 1 to 8, Either - * Added vending machine demo - * Added Option.toValidation - * Added map and contramap for F and F2. - * Added partial application for F1. -* Fixes - * Various Javadoc issues - * Large inserts into TreeMap - * Javadoc support for Java 8 - * Null parameter NullPointerException for Show.anyShow - * Exception propagation in test data generators - * Product memoisation for arities 1 to 8 - * ClassCastException in class fj.data.Java - -4.1 -=== -Released: 30 May, 2014 - -* Support Java 7 by removing default methods. Methods on class C with default methods moved to static methods on new class ${C}Functions. - -4.0 -=== -Released: 30 May, 2014 - -* Merged changes from 3.2 -* Minimal changes to support Java 8. -* Changed abstract classes to interfaces with default methods (P1, F1 to F8). - -3.2 -=== -Released: 30 May, 2014 - -* Added methods to HashMap: toList, toStream, toOption, toArray, map, mapKeys, foreach, convert to/from java.util.Map. -* Convert from java.util.List to List. -* Fixed findChild method in TreeZipper to handle empty stream. -* Fixed stack overflow when sorting and zipping. -* Fixed stack overflow in Stream's bind method. -* Small Javadoc fixes. - -3.1 -=== -Released: May 2012 - -3.0 -=== -Released: Jun 2010 - -2.21 -==== -Released Feb, 2010 - -* Bug fixes -* Immutable 2-3 finger tree - -2.20 -=== -Unknown - -2.19 -=== -Released March 2009 - -* Comonadic operations on Stream, Tree and others -* Database monad (java.sql.Connection as a functor) -* Natural Number data type -* The Constant Arrow ($) -* Immutable Tree Map -* A parallel quick-sort using Functional Java actors - diff --git a/etc/release-notes/release-notes-2.17.adoc b/etc/release-notes/release-notes-2.17.adoc new file mode 100644 index 00000000..d2763b22 --- /dev/null +++ b/etc/release-notes/release-notes-2.17.adoc @@ -0,0 +1,5 @@ += Release 2.17 + +Released: 12 Dec 2008 + +* See SVN log diff --git a/etc/release-notes/release-notes-2.18.adoc b/etc/release-notes/release-notes-2.18.adoc new file mode 100644 index 00000000..1d34f5c6 --- /dev/null +++ b/etc/release-notes/release-notes-2.18.adoc @@ -0,0 +1,5 @@ += Release 2.18 + +Released: 23 Mar 2009 + +* See SVN log diff --git a/etc/release-notes/release-notes-2.19.adoc b/etc/release-notes/release-notes-2.19.adoc new file mode 100644 index 00000000..7dacbacd --- /dev/null +++ b/etc/release-notes/release-notes-2.19.adoc @@ -0,0 +1,11 @@ += Release 2.19 + +Released: March 2009 + +* Comonadic operations on Stream, Tree and others +* Database monad (java.sql.Connection as a functor) +* Natural Number data type +* The Constant Arrow ($) +* Immutable Tree Map +* A parallel quick-sort using Functional Java actors + diff --git a/etc/release-notes/release-notes-2.20.adoc b/etc/release-notes/release-notes-2.20.adoc new file mode 100644 index 00000000..f3dba40a --- /dev/null +++ b/etc/release-notes/release-notes-2.20.adoc @@ -0,0 +1,17 @@ += Release 2.20 + +Released: July 2009 + +The highlight of this release is a parallel module built on top of +actors for doing some very clever and high-level parallel programming. +e.g. An implementation of the parallel map/reduce algorithm +(parFoldMap) and APIs for making use of actors easier for you, the user. + +Other new bits includes: + +* A heap of bug fixes, particularly on Stream (and therefore, many of +its dependencies) +* LazyString -- a lazy sequence of characters +* Function Wrappers +* Improvements to the Tree and Zipper implementations +* Other tidbits and additional APIs diff --git a/etc/release-notes/release-notes-2.21.adoc b/etc/release-notes/release-notes-2.21.adoc new file mode 100644 index 00000000..f44b144e --- /dev/null +++ b/etc/release-notes/release-notes-2.21.adoc @@ -0,0 +1,6 @@ += Release 2.21 + +Released: Feb, 2010 + +* Bug fixes +* Immutable 2-3 finger tree diff --git a/etc/release-notes/release-notes-2.22.adoc b/etc/release-notes/release-notes-2.22.adoc new file mode 100644 index 00000000..f0df973e --- /dev/null +++ b/etc/release-notes/release-notes-2.22.adoc @@ -0,0 +1,7 @@ += Release 2.22 + +Released: March 2010 + +* Bug fixes +* Documentation fixes +* Built against JDK 1.5 (not 1.6) diff --git a/etc/release-notes/release-notes-2.23.adoc b/etc/release-notes/release-notes-2.23.adoc new file mode 100644 index 00000000..fbcdb436 --- /dev/null +++ b/etc/release-notes/release-notes-2.23.adoc @@ -0,0 +1,6 @@ += Release 2.23 + +Released: TODO + +Changes: TODO + diff --git a/etc/release-notes/release-notes-2.5.adoc b/etc/release-notes/release-notes-2.5.adoc new file mode 100644 index 00000000..d15a0869 --- /dev/null +++ b/etc/release-notes/release-notes-2.5.adoc @@ -0,0 +1,7 @@ += Release 2.5 + +Released: 1 May 2008 + +Changes + +* Unknown diff --git a/etc/release-notes/release-notes-3.0.adoc b/etc/release-notes/release-notes-3.0.adoc new file mode 100644 index 00000000..a685b98a --- /dev/null +++ b/etc/release-notes/release-notes-3.0.adoc @@ -0,0 +1,7 @@ += Release 3.0 + +Released: Jun 2010 + +A crucial change has been made to the F interfaces, which are now abstract classes and containing useful methods. This means some other methods (fj.Function) are redundant and will be removed in a release soon (perhaps with a @Deprecated first). + +There are other minor additions and bug fixes. diff --git a/etc/release-notes/release-notes-3.1.adoc b/etc/release-notes/release-notes-3.1.adoc new file mode 100644 index 00000000..0c7e1071 --- /dev/null +++ b/etc/release-notes/release-notes-3.1.adoc @@ -0,0 +1,33 @@ += Release 3.1 + +Released: May 2012 + +Administrivia: + +* Sources have been moved to GitHub: https://github.com/functionaljava/functionaljava +* Built has been converted to SBT + +New features: + +* List.mapMOption - list traversal in the Option monad. +* List.allEqual - Returns whether or not all elements in the list are equal. +* Added a monoid for Double. +* Tree.bottomUp - Grows a tree from another tree by folding it from the bottom up. +* Strings.isNotNullOrEmpty - does what it says. +* Ord.hashOrd - an order instance that uses hashCode() for the order. +* Ord.hashEqualsOrd - same as above but also uses .equals() +* Set.set - A vararg Set factory method. +* Added first-class functions for Set intersect, union, and minus. +* First-class LazyString.toString. +* Added hashCode() and equals() to Option. +* Iteratees and an IO monad. +* Trampoline - a general solution for tail-call elimination in Java. +* List.equals(), List.hashCode(), and List.toString(). + +Bug fixes: + +* Stream.inits should always be nonempty. +* Stream was not compiling in Eclipse. +* Stream.length is now tail-recursive. +* TreeZipper.delete was flipping lefts and rights. +* Fixed naturalOrd Javadoc. diff --git a/etc/release-notes/release-notes-3.2.1.adoc b/etc/release-notes/release-notes-3.2.1.adoc new file mode 100644 index 00000000..09ea6007 --- /dev/null +++ b/etc/release-notes/release-notes-3.2.1.adoc @@ -0,0 +1,29 @@ + += Release 3.2.1 + +Released 24 August 2015 + +== Enhancements + +None. + +== Fixes + +None. + +== Internal + +This release targets the 1.6 virtual machine. + +== Breaking Changes + +None. + +== Documentation + +None. + +== Contributors + +* Mark Perry. + diff --git a/etc/release-notes/release-notes-3.2.adoc b/etc/release-notes/release-notes-3.2.adoc new file mode 100644 index 00000000..7c10f6fb --- /dev/null +++ b/etc/release-notes/release-notes-3.2.adoc @@ -0,0 +1,10 @@ += Release 3.2 + +Released: 30 May, 2014 + +* Added methods to HashMap: toList, toStream, toOption, toArray, map, mapKeys, foreach, convert to/from java.util.Map. +* Convert from java.util.List to List. +* Fixed findChild method in TreeZipper to handle empty stream. +* Fixed stack overflow when sorting and zipping. +* Fixed stack overflow in Stream's bind method. +* Small Javadoc fixes. diff --git a/etc/release-notes/release-notes-4.0.adoc b/etc/release-notes/release-notes-4.0.adoc new file mode 100644 index 00000000..e685d91d --- /dev/null +++ b/etc/release-notes/release-notes-4.0.adoc @@ -0,0 +1,8 @@ + += Release 4.0 + +Released: 30 May, 2014 + +* Merged changes from 3.2 +* Minimal changes to support Java 8. +* Changed abstract classes to interfaces with default methods (P1, F1 to F8). diff --git a/etc/release-notes/release-notes-4.1.adoc b/etc/release-notes/release-notes-4.1.adoc new file mode 100644 index 00000000..484c9e8e --- /dev/null +++ b/etc/release-notes/release-notes-4.1.adoc @@ -0,0 +1,7 @@ + += Release 4.1 + +Released: 30 May, 2014 + +* Support Java 7 by removing default methods. Methods on class C with default methods moved to static methods on new class ${C}Functions. + diff --git a/etc/release-notes/release-notes-4.2.adoc b/etc/release-notes/release-notes-4.2.adoc new file mode 100644 index 00000000..a0400a27 --- /dev/null +++ b/etc/release-notes/release-notes-4.2.adoc @@ -0,0 +1,47 @@ + += Release 4.2 + +Released: 20 December, 2014 + +== Enhancements + +* Added Java 8 examples +* Added new website +* Added Option.none_() +* Gradle 2.2.1 support with wrapper +* Added to Strings: isNullOrEmpty, isNullOrBlank and isNotNullOrBlank +* Added Try with arity 0-8 for lambdas that throw exceptions +* Added Effect with arity 0-8 for lambdas with a void return +* Added TryEffect with arity 0-8 for lambdas with a void return that throw an Exception. +* Conversion from Java 8 types to Functional Java types and vice-versa +* Added monadic IO methods +* Support instantiation of IO with lambdas +* Added first class cons_ for List +* Added partial application of first parameter for F 1-8 +* Added SafeIO that encodes IOExceptions into Validations +* Added simple RNG +* Added Reader, Writer and State +* Deprecated $._ +* Added Array.scan family of methods +* Support instantiating P1 values using lambdas to P.lazy +* Added toString for Validation, P arities 1 to 8, Either +* Added vending machine demo +* Added Option.toValidation +* Added map and contramap for F and F2. +* Added partial application for F1. +* Added filter and uncurried foldLeft and foldRight to FingerTree. +* Added foldLeft, foldRight and map to Seq. +* Accumulate Validation errors in a List. +* Convert from fj.data.Stream to java.util.Stream and vice versa. +* Added groupBy on List. + +== Fixes + +* Various Javadoc issues +* Large inserts into TreeMap +* Javadoc support for Java 8 +* Null parameter NullPointerException for Show.anyShow +* Exception propagation in test data generators +* Product memoisation for arities 1 to 8 +* ClassCastException in class fj.data.Java +* Fixed performance of Set.member. diff --git a/etc/release-notes/release-notes-4.3.adoc b/etc/release-notes/release-notes-4.3.adoc new file mode 100644 index 00000000..094eb08a --- /dev/null +++ b/etc/release-notes/release-notes-4.3.adoc @@ -0,0 +1,67 @@ + += Release 4.3 + +Released: 14 February 2015 + +== Enhancements + +* Added toInt and fromInt to fj.Ordering (#71) +* Added Java 8 collectors (#69) +* Made Seq.empty() and List.nil() return singleton instances (#70) +* Made anonymous classes using lamdas (#67) +* Improved show for list, tree, set, treemap, stream, array, V arity 2 to 8, HList (#77) +* Added TreeMap.splitLookup (#4) +* Add union to TreeMap (#83) +* Added List.isSingle (#84) +* Added Option traverse family of methods (#85) +* Added equals, hashCode and toString to classes P's, Array, Either, List, Option, Seq, Set, Stream, Tree, TreeMap, Validation (#75, #86) +* Added release notes as asciidoc (#92) +* Added F0 interface (#93) +* Added wrapper functions for function interfaces arity 1 and 2 (#94) +* Enhanced implementation of equals for standard classes (#95) +* Added traverse method family to P1, P2, Either, List, Validation (#80) +* Enhanced integration with basic Java types (#99) +* Made artifact OSGI compatible, suitable as Eclipse plugin (#72) +* Added predicate combinators (#98) + +== Fixes + +* Fixed TreeMap.split (#4, #79) +* Fixed Option.fromNull javadoc (#73) +* Fixed Try javadoc (#91) +* Fixed stack overflow in TreeMap.update (#97) +* Fixed concurrency issue with P1.memo (#105) + +== Breaking Changes + +* #93 added the F0 interface which P1 implements. This could break code where the _1 method for a new P1 called the method 'f' outside P1's scope. Making P1 implement F0 means that P1 now has an 'f' method which hides the visibility of the previously visible outer 'f'. You can fix this by using P.lazy, as per the example below. + +[source,java] +---- +// old code +return fromString(s.some()).append(new P1>() { + public Stream _1() { + return f(reader); // call outer f, 4.3 code change breaks this + } +}); + +// new code +return fromString(s.some()).append(P.lazy(u -> f(reader))); // outer f now visible +---- + + +== Documentation + +* Added contributors from commit log. + +== Contributors + +* Mark Perry +* Amar Potghan +* Dobes Vandermeer +* Paul Horn +* Alexey Mz +* daneko +* Greg Row + + diff --git a/etc/release-notes/release-notes-4.4.adoc b/etc/release-notes/release-notes-4.4.adoc new file mode 100644 index 00000000..54184076 --- /dev/null +++ b/etc/release-notes/release-notes-4.4.adoc @@ -0,0 +1,90 @@ + += Release 4.4 + +Released: 6 July 2015 + +== Enhancements + +* Added optics lenses inspired from the Monocle Scala library, instances for List and P2 (#130, #131, #132, #159). +* Added functionality to FingerTree and Seq: head, last, tail, init, split, split1, update, toJavaList, Iterable (#140). +* Added OrdComparator and fixed TreeMap.toMutableMap (#110). +* Added bimap to Either (#121). +* Added monoid composition. +* Changed comap methods to contramap (#142, #170) +* Provide hard, soft and weak memoisation implementations, default to weak (#153, #151). +* Added optics (see the Scala Monocle library https://github.com/julien-truffaut/Monocle and the Haskell lens lbrary https://github.com/ekmett/lens for an overview) (#154). +* Added Validation.sequence (#163, #167). +* Add PropertyTestRunner (#144). +* Added Equal.notEq, List.isPrefixOf, List.isSuffixOf (#146). +* Made enhancements to support Real World Haskell example (#155) + ** made P1.toString lazy + ** added IOFunctions.interact + ** added IOFunctions.getContents + ** added map, bind and eval to LazyString + ** added IO wrapper with static methods from IOFunctions as instance methods + ** Stream.take evaluates one those items necessary (one less than currently) + ** Made Stream.toString lazy + ** Added Strings methods lines and unlines + ** Made IOFunctions.sequenceWhile lazy + ** Changed IOFunctions io to be fromF and fromTry. +* Enhanced Property to run individual tests (#156). +* Added Seq.isNotEmpty (#158). +* Added List.partition (#161). +* Added Validation.partition, arbitrary validations for testing (#165). +* Added Validation.sequence (using semigroup) (#163, #167). + +== Fixes + +* Fixed LazyString.toString for Android (#109) +* Fixed exhausted ScalaCheck tests (#115). +* Fixed overflow in standard Rand (#112). +* Fixed equals for anonymous classes (#122, #125). +* Fixed List.init (#128, #129). +* Fixed Effect.f ignoring argument (#133, #134). +* Made Effect.comap static (#135, #134). +* Fixed Class.inheritance infinite list (#137). +* Fixed Stream.toString to human readable representation (#138). +* Fixed P1.memo cache in threaded environment (#141, #108). +* Fixed Stream join and bind for infinite streams (#140). +* Fixed Validation.sequence and renamed to sequenceNonCumulative (#162). +* Scalacheck property Stream.bindStackOverflow sometimes fails (#168, #169). +* Fixed bug in FingerTree.append (#144). +* Fixed arrayShow (#145). +* Fixed Stream.bindStackOverflow test that occasionally failed (#157). + +== Internal + +* Added Travis build (#115). +* Upgraded to Gradle 2.4 (#143, #118). +* Added Gradle uptodate plugin (#119). +* Updated Scala and ScalaCheck to 2.11.6 and 1.12.2 respectively (#120). +* Replaced P1 by F0 in various APIs (#136). +* Split quickcheck property testing into separate module (#147, #148). +* Updated libraries used: Uptodate 1.5.1, Retrolambda plugin 3.2.0, JUnit 4.12, Scala 2.11.7, Scalacheck 1.12.4 (#171, #172). +* Fixed unnecessary P1.memo wrapping (#150). +* Added property tests for List, Stream and Array (#158). + +== Breaking Changes + +* Comap methods renamed to contramap. +* Split quickcheck into separate module. +* Validation.sequence renamed to sequenceNonCumulative. +* Changed IOFunctions io to be fromF and fromTry. + +== Documentation + +None. + +== Contributors + +* Mark Perry +* Jean-Baptiste Giraudeau +* Zheka Kozlov +* daneko +* Dobes Vandermeer +* Kenji Yoshida +* Runar Bjarnason +* Paul Horn +* John Sullivan + + diff --git a/etc/release-notes/release-notes-4.5.adoc b/etc/release-notes/release-notes-4.5.adoc new file mode 100644 index 00000000..3cbc7259 --- /dev/null +++ b/etc/release-notes/release-notes-4.5.adoc @@ -0,0 +1,89 @@ + += Release 4.5 + +Released: 15 February 2016 + +== Enhancements + +* Added Seq toList and delete (#198). +* Added functions to P1: map_, liftM2, sequence (#200). +* Added liftM2 to Option (#201). +* Added headOption and tailOption to List (#202). +* Replaced usage of P1 with F0 in Stream orTail, sequence and cons (#203). +* Added Arbitrary.arbSet (48d4c8358f1257e2126726be0808b48568f372bf). +* Added to Arbitrary arbitrary lists for integer, string, boolean and double (#183). +* Updated Stream.toList using List.Buffer for performance (#180). +* Added java-core module for simple handling of core Java types, e.g. List map, bind, filter, fold (4611c3524eca1786c112fcae0a727f2243b2d492, 13ebd6f22db7a8bf8247f0f4ea4ac3cd8afef4b9). +* Added Tree isLeaf and length (#185). +* Added min and max to Set (#176). +* Added min, minKey, max and maxKey for TreeMap. Added arbitrary TreeMap's (#176). +* Added ordering a P2 by first or second element in the tuple (#175). +* Added Set.toStream (#175). +* Added TreeMap.toList (#175). +* Added toListReverse and toStreamReverse for Set and TreeMap (#177). +* Added headOption to Seq and FingerTree. Added length to FingerTree (7e1d3a9300af84b3c4c5204e4b7a5a0e89c44303). +* Added Stream.enumerationStream (0028ef0f8e6a36b77cbd75b29e92d77eacc167bd). +* Support use of FunctionalJava on Java 6 runtime (67f1fc9f2c35479bf18d9ef948062e01ef740d5e). +* Added Void, a logically uninhabited type (#193). +* Log n multiply for Semigroup/Monoid (61ec2b09e5acc629f334d7c4264a92b99dd9fd3b). +* Added coverage badge. +* Added Stream.toJavaList +* Added support to create fj.data types from standard Java iterable, iterator and array types. +* Added support to convert to standard java.util.* classes for standard fj.data types where similar java.util class exists. +* Added difference list support (DList, #214). +* Made Array.ImmutableProjection static (#210). +* Added Gitter room and badge at the top of the main Github page. + +== Fixes + +* Fixed constructors of fixed length monomorphic traversal (#179). +* Fixed defect in Buffer.copy (#181). +* Fixed order of foldMap for Set which switch the left and right branches (#205). +* Fixed List.intersperse to use heap, not stack space (#188). +* Fixed Validation.accumulate for 4 validations (#190). +* Avoid stackoverflow for List.splitAt (0ca0e13a4106e7dad676b9c389e7ea3b79854c62). +* Avoid stackoverflow for List.take (aec491c84c7c7761f8a1ddddd67b4678b485c1af). +* Resolved overloaded List methods (d3e99ced4ec5f3ae4c4066a02a50da55ffc981b2). + +== Internal + +* Added @SafeVargs to suppress various unchecked warnings (#174). +* Suppressed various unchecked compiler warnings (9853c6a26309cf9f4936d431a6bc65b4c37550b1). +* Added compiler warnings for unchecked and deprecation (#204). +* Simplified code to avoid function creation and inner static class (#179). +* Upgraded to Gradle 2.10. +* Added example of badly creating a Stream from an iterator (d38a694f9f1728417cb178c685850f627b10bf5b). +* Configure Retro Lambda using variables (6aa26bcb56dd43d36bede2e8c34cbb21f69c1c8a). +* Updated package fj, fj.control, fj.data, fj.data.fingertrees, fj.data.hlist, fj.data.vector, fj.function, fj.parser to use lambdas (#206). +* Added test coverage report, run "gradlew jacocoRootReport coveralls", report is under root/build/reports/jacoco/coverage/html/index.html (#207). +* Updated Travis CI to use latest Java 8 (8ddc11546badbeafb0b16bc1e0fb922b06f23079). +* Replaced usage of new P1 with P.lazy (2d6a8dd4412220a749331b6ffec45bd720ac7479). +* Replaced P1 with F0 instances where possible (5b8248c51445e92aa8c8ff241e717f909a5bdd63, 0ca5248a3016209c9e62df509a79ed9432421322). +* Replace anonymous F2 instances with lambdas (b438b1b89705f6d0bfd5e7c31bd217d173c43dfc). +* Replace anonymous F3 instances with lambdas (721118902a5af76665df347b073534debf384f69). +* Added coverall integration, submodules not consolidated into single report. +* Replaced annonymous P2 with static constructor (#189, 92f663384541dfbea013c14b7f60951d925bcd26). +* Created performance module to keep code demonstrating performance based reasoning. +* Added JUnit for Doubles (#226). + +== Breaking Changes + +* Made Array.ImmutableProjection static (#210). This previously forced usage by `Array.ImmutableProjection` or `Array.ImmutableProjection`. + +== Documentation + +* Added release process doc (#174). + +== Contributors + +* Mark Perry +* Zheka Kozlov +* Jean-Baptiste Giraudeau +* Shintaro Murakami +* @daneko +* Gabor Liptak +* Michal Samek +* Clinton Selke +* Roy Brokvam + + diff --git a/etc/release-notes/release-notes-4.6.adoc b/etc/release-notes/release-notes-4.6.adoc new file mode 100644 index 00000000..a33c1214 --- /dev/null +++ b/etc/release-notes/release-notes-4.6.adoc @@ -0,0 +1,74 @@ + += Release 4.6 + +Released: 3 July 2016 + +== Enhancements + +* 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 + +* 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 + +* 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 + +* Removed deprecated Equal.shallowEquals0 (#239). +* Removed use of Arbitrary wrapper (#271). + +== Deprecations + +* Array.toJavaArray (#241). + +== Documentation + +* Added IO walkthrough (as per enhancements, #254). + +== Contributors + +* 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-notes/release-notes-template.adoc b/etc/release-notes/release-notes-template.adoc new file mode 100644 index 00000000..22dbc552 --- /dev/null +++ b/etc/release-notes/release-notes-template.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 new file mode 100644 index 00000000..f5699a5b --- /dev/null +++ b/etc/release-process.txt @@ -0,0 +1,108 @@ + +Release Process +=============== + +Go through the issues and pull requests and set the Label and Milestone field. Add information to /etc/release-notes/release-notes-.adoc. + +Update gradle.properties: +* set isSnapshot to false +* Set signingEnabled to true + + +Run the build command: +gradlew clean build publishAllPublicationsToMavenRepository + +Login to Sonatype and verify the release: +* Login to https://oss.sonatype.org +* Choose Staging Repositories and find the Functional Java 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: 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. + +Create tag: +* Create tag using gitk with the format v. +* Push the tag with: git push --tags + +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, 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 deccec26..b1159fc5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +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=http\://services.gradle.org/distributions/gradle-1.11-bin.zip -distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-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 new file mode 100644 index 00000000..f6a6146a --- /dev/null +++ b/java-core/build.gradle @@ -0,0 +1,17 @@ + +archivesBaseName = "${project.projectName}-${project.name}" + +ext { + signModule = true + uploadModule = true +} + +dependencies { + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime +} + +performSigning(signingEnabled, signModule) +configureUpload(signingEnabled, signModule, uploadModule) + diff --git a/java-core/src/main/java/fj/java/util/ListUtil.java b/java-core/src/main/java/fj/java/util/ListUtil.java new file mode 100644 index 00000000..ffab7968 --- /dev/null +++ b/java-core/src/main/java/fj/java/util/ListUtil.java @@ -0,0 +1,30 @@ +package fj.java.util; + +import fj.F; +import fj.F2; + +import java.util.List; + +public class ListUtil { + + public static List map(List list, F f) { + return fj.data.List.iterableList(list).map(f).toJavaList(); + } + + public static List filter(List list, F f) { + return fj.data.List.iterableList(list).filter(f).toJavaList(); + } + + public static B fold(List list, F2 f, B b) { + return fj.data.List.iterableList(list).foldLeft(f, b); + } + + public static List flatMap(List list, F> f) { + return fj.data.List.iterableList(list).bind(a -> fj.data.List.iterableList(f.f(a))).toJavaList(); + } + + public static List bind(List list, F> f) { + return flatMap(list, f); + } + +} diff --git a/java-core/src/test/java/fj/EmptyTest.java b/java-core/src/test/java/fj/EmptyTest.java new file mode 100644 index 00000000..1c126bfa --- /dev/null +++ b/java-core/src/test/java/fj/EmptyTest.java @@ -0,0 +1,14 @@ +package fj; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.Assert; + +public class EmptyTest { + + @Ignore @Test + public void missing() { + Assert.fail("not implemented"); + + } +} diff --git a/java8/src/main/java/fj/data/Java8.java b/java8/src/main/java/fj/data/Java8.java deleted file mode 100644 index c22cd149..00000000 --- a/java8/src/main/java/fj/data/Java8.java +++ /dev/null @@ -1,134 +0,0 @@ -package fj.data; - -import fj.*; -import fj.function.Try0; -import fj.function.Try1; -import fj.function.Try2; - -import java.util.Optional; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.StreamSupport; - -/** - * Created by mperry on 3/06/2014. - */ -public final class Java8 { - - private Java8() { - throw new UnsupportedOperationException(); - } - - public static P1 Supplier_P1(final Supplier s) { - return Java8.Supplier_P1().f(s); - } - - public static F, P1> Supplier_P1() { - return s -> P.lazy(u -> s.get()); - } - - public static Supplier P1_Supplier(final P1 p) { - return Java8.P1_Supplier().f(p); - } - - public static F, Supplier> P1_Supplier() { - return (p) -> () -> p._1(); - } - - public static F Function_F(final Function f) { - return Java8.Function_F().f(f); - } - - public static F, F> Function_F() { - return f -> a -> f.apply(a); - } - - public static Function F_Function(final F f) { - return Java8.F_Function().f(f); - } - - public static F, Function> F_Function() { - return f -> a -> f.f(a); - } - - public static F2 BiFunction_F2(final BiFunction f) { - return Java8.BiFunction_F2().f(f); - } - - public static F, F2> BiFunction_F2() { - return f -> (a, b) -> f.apply(a, b); - } - - public static BiFunction F2_BiFunction(final F2 f) { - return Java8.F2_BiFunction().f(f); - } - - public static F, BiFunction> F2_BiFunction() { - return f -> (a, b) -> f.f(a, b); - } - - static public Supplier> TryCatch0_Supplier(final Try0 t) { - return Java8.TryCatch0_Supplier().f(t); - } - - static public F, Supplier>> TryCatch0_Supplier() { - return t -> () -> Try.f(t)._1(); - } - - static public Function> TryCatch1_Function(final Try1 t) { - return Java8.TryCatch1_Function().f(t); - } - - static public F, Function>> TryCatch1_Function() { - return t -> a -> Try.f(t).f(a); - } - - static public BiFunction> TryCatch2_BiFunction(final Try2 t) { - return Java8.TryCatch2_BiFunction().f(t); - } - - static public F, BiFunction>> TryCatch2_BiFunction() { - return t -> (a, b) -> Try.f(t).f(a, b); - } - - static public Option Optional_Option(final Optional o) { - return Java8.Optional_Option().f(o); - } - - static public F, Option> Optional_Option() { - return o -> o.isPresent() ? Option.fromNull(o.get()) : Option.none(); - } - - static public Optional Option_Optional(final Option o) { - return Java8.Option_Optional().f(o); - } - - static public F, Optional> Option_Optional() { - return o -> o.isSome() ? Optional.ofNullable(o.some()) : Optional.empty(); - } - - static public F, F> Consumer_F() { - return c -> Consumer_F(c); - } - - public static F Consumer_F(Consumer c) { - return a -> { - c.accept(a); - return Unit.unit(); - }; - } - - static public java.util.stream.Stream Stream_JStream(fj.data.Stream s) { - Spliterator sit = Spliterators.spliteratorUnknownSize(s.iterator(), 0); - return StreamSupport.stream(sit, false); - } - - static public F, java.util.stream.Stream> Stream_JStream() { - return s -> Stream_JStream(s); - } - -} diff --git a/lib.gradle b/lib.gradle index 39ce7a7e..5ff91f01 100644 --- a/lib.gradle +++ b/lib.gradle @@ -20,7 +20,7 @@ String findJavaCommand(String command) { Boolean doSigning(String signingAllowed, Boolean doModule) { def b = signingAllowed.trim() == "true" && doModule -// println("signModule: ${project.name} signingEnabled: $signingAllowed module: $doModule") +// println("signModule: ${project.name} signingAllowed: $signingAllowed doModule: $doModule") b } @@ -31,50 +31,122 @@ void performSigning(String signingAllowed, Boolean doModule) { } } -void configureUpload(String signingEnabled, Boolean signModule) { +def customisePom(pom, gradleProject) { + pom.withXml { + def root = asNode() - uploadArchives { - enabled = false - repositories { - mavenDeployer { - if (doSigning(signingEnabled, signModule)) { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - } + // add all items necessary for maven central publication + root.children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST - repository(url: sonatypeUploadUrl) { - authentication(userName: sonatypeUsername, password: sonatypePassword) + name project.pomProjectName + description project.projectDescription + url project.projectUrl + organization { + name project.pomOrganisation + url project.projectUrl + } + issueManagement { + system 'GitHub' + url project.issueUrl + } + licenses { + license { + name project.licenseName + url project.licenseUrl + distribution 'repo' } - pom { - groupId = project.group - project { - name pomProjectName - packaging 'jar' - description projectDescription - url projectUrl - organization { - name pomOrganisation - url projectUrl - } - scm { - url scmUrl - } - licenses { - license { - name "The BSD3 License" - url "https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE" - distribution 'repo' + } + scm { + url project.githubUrl + connection project.scmGitFile + developerConnection project.scmSshGitFile + } + } + } +} + + +void configureUpload(String signingEnabled, Boolean signModule, Boolean uploadModule) { + + if (uploadModule) { + + publishing { + + publications { + + mavenJava(MavenPublication) { + groupId project.group + artifactId project.name + version project.version + + from components.java + + customisePom(pom, rootProject) + + artifact sourcesJar + artifact javadocJar + + if (doSigning(signingEnabled, signModule)) { + // sign the pom + pom.withXml { + def pomFile = file("${project.buildDir}/generated-pom.xml.asc") + writeTo(pomFile) + def pomAscFile = signing.sign(pomFile).signatureFiles[0] + artifact(pomAscFile) { + classifier = null + extension = 'pom.asc' } + pomFile.delete() } - developers { - developer { - email primaryEmail + + // sign the artifacts + project.tasks.signArchives.signatureFiles.each { + artifact(it) { + def matcher = it.file =~ /-(sources|javadoc|jre8|jre9)\.jar\.asc$/ + if (matcher.find()) { + classifier = matcher.group(1) + } else { + classifier = null + } + extension = 'jar.asc' } } } + + } + + } + + repositories { + maven { + url project.sonatypeUploadUrl + credentials { + username sonatypeUsername + password sonatypePassword + } } } + + } + + model { + tasks.publishMavenJavaPublicationToMavenLocal { + dependsOn(project.tasks.signArchives) + } + tasks.publishMavenJavaPublicationToMavenRepository { + dependsOn(project.tasks.signArchives) + } + tasks.publish { + dependsOn(project.tasks.build) + } +// tasks.install { +// dependsOn(project.tasks.build) +// } } + } + } ext { diff --git a/performance/build.gradle b/performance/build.gradle new file mode 100644 index 00000000..7efbde2e --- /dev/null +++ b/performance/build.gradle @@ -0,0 +1,6 @@ + +dependencies { + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime +} diff --git a/performance/src/main/java/fj/data/DListPerformance.java b/performance/src/main/java/fj/data/DListPerformance.java new file mode 100644 index 00000000..8294a884 --- /dev/null +++ b/performance/src/main/java/fj/data/DListPerformance.java @@ -0,0 +1,97 @@ +package fj.data; + +/** + * Difference List performance benchmarks comparing DList to Seq and List + * over 10 runs for the methods range, append and unbox. + * + * @author clintonselke + */ +public class DListPerformance { + + static final int TOTAL_RUNS = 10; + + private interface BenchmarkMethods { + C range(int from, int to); + C append(C a, C b); + List unbox(C a); + } + + private static final BenchmarkMethods> listMethods = new BenchmarkMethods>() { + @Override + public List range(int from, int to) { + return List.range(from, to); + } + @Override + public List append(List a, List b) { + return a.append(b); + } + @Override + public List unbox(List a) { + return a; + } + }; + + private static final BenchmarkMethods> seqMethods = new BenchmarkMethods>() { + @Override + public Seq range(int from, int to) { + return Seq.iterableSeq(List.range(from, to)); + } + @Override + public Seq append(Seq a, Seq b) { + return a.append(b); + } + @Override + public List unbox(Seq a) { + return a.toList(); + } + }; + + private static final BenchmarkMethods> dListMethods = new BenchmarkMethods>() { + @Override + public DList range(int from, int to) { + return DList.listDList(List.range(from, to)); + } + @Override + public DList append(DList a, DList b) { + return a.append(b); + } + @Override + public List unbox(DList a) { + return a.run(); + } + }; + + private static double benchmark(BenchmarkMethods methods) { + long msStart = System.currentTimeMillis(); + + for (int runNumber = 0; runNumber < TOTAL_RUNS; ++runNumber) { + final C xs = methods.range(0, 100); + C r = xs; + for (int i = 1; i < 2000; ++i) { + r = methods.append(r, xs); + } + List r2 = methods.unbox(r); + for (Integer x : r2) {} + } + long msEnd = System.currentTimeMillis(); + return (msEnd - msStart) / ((double) TOTAL_RUNS); + } + + public static void main(String[] params) { + System.out.println("Starting difference list (DList) performance benchmark..."); + // warm up + System.out.println("warm up..."); + benchmark(listMethods); + benchmark(seqMethods); + benchmark(dListMethods); + // actual run + System.out.println("running benchmark..."); + double listTime = benchmark(listMethods); + double seqTime = benchmark(seqMethods); + double dListTime = benchmark(dListMethods); + System.out.println("Average over " + TOTAL_RUNS + " runs..."); + System.out.println("List: " + listTime + "ms"); + System.out.println("Seq: " + seqTime + "ms"); + System.out.println("DList: " + dListTime + "ms"); + } +} diff --git a/performance/src/main/java/fj/data/dummy/DummyClass.java b/performance/src/main/java/fj/data/dummy/DummyClass.java new file mode 100644 index 00000000..a94dcdde --- /dev/null +++ b/performance/src/main/java/fj/data/dummy/DummyClass.java @@ -0,0 +1,5 @@ +package fj.data.dummy; + +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 new file mode 100644 index 00000000..49722bcd --- /dev/null +++ b/performance/src/test/java/fj/data/dummy/DummyTest.java @@ -0,0 +1,14 @@ +package fj.data.dummy; + +import org.junit.Ignore; +import org.junit.Test; + +public class DummyTest { + + @Test + @Ignore + public void test1() { + + } + +} diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle new file mode 100644 index 00000000..396d19d4 --- /dev/null +++ b/props-core-scalacheck/build.gradle @@ -0,0 +1,36 @@ + +archivesBaseName = "${project.projectName}-${project.name}" + +apply plugin: 'scala' + +ext { +// 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" + + signModule = true +} + +dependencies { + api project(":core") + api "org.scala-lang:scala-library:$scalaVersion" + api "org.scalacheck:scalacheck_$scalacheckScalaVersion:$scalacheckVersion" + + testImplementation junitCompile + testRuntimeOnly junitRuntime +} + +tasks.withType(ScalaCompile) { + scalaCompileOptions.additionalParameters = ["-feature", "-language:implicitConversions", "-language:postfixOps"] +} + +performSigning(signingEnabled, signModule) +configureUpload(signingEnabled, signModule, project.uploadModule) diff --git a/props-core-scalacheck/src/main/scala/fj/ArbitraryP.scala b/props-core-scalacheck/src/main/scala/fj/ArbitraryP.scala new file mode 100644 index 00000000..8da8d864 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/ArbitraryP.scala @@ -0,0 +1,14 @@ +package fj + +import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.arbitrary + +object ArbitraryP { + implicit def arbitraryP1[A](implicit a: Arbitrary[A]): Arbitrary[P1[A]] = + Arbitrary(arbitrary[A].map(a => new P1[A] { + def _1 = a + })) + + implicit def arbitraryP2[A, B](implicit a: Arbitrary[A], b: Arbitrary[B]): Arbitrary[P2[A,B]] = + Arbitrary(arbitrary[A].flatMap(a => arbitrary[B].map(b => P.p(a, b)))) +} \ No newline at end of file diff --git a/props-core-scalacheck/src/main/scala/fj/ArbitraryUnit.scala b/props-core-scalacheck/src/main/scala/fj/ArbitraryUnit.scala new file mode 100644 index 00000000..ca68da1c --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/ArbitraryUnit.scala @@ -0,0 +1,9 @@ +package fj + +import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.arbitrary + +object ArbitraryUnit { + implicit def arbitraryUnit: Arbitrary[Unit] = + Arbitrary(Unit.unit()) +} \ No newline at end of file diff --git a/tests/src/test/scala/fj/control/parallel/ArbitraryParModule.scala b/props-core-scalacheck/src/main/scala/fj/control/parallel/ArbitraryParModule.scala similarity index 100% rename from tests/src/test/scala/fj/control/parallel/ArbitraryParModule.scala rename to props-core-scalacheck/src/main/scala/fj/control/parallel/ArbitraryParModule.scala diff --git a/tests/src/test/scala/fj/control/parallel/ArbitraryStrategy.scala b/props-core-scalacheck/src/main/scala/fj/control/parallel/ArbitraryStrategy.scala similarity index 83% rename from tests/src/test/scala/fj/control/parallel/ArbitraryStrategy.scala rename to props-core-scalacheck/src/main/scala/fj/control/parallel/ArbitraryStrategy.scala index 4ff0dd8d..a3e49011 100644 --- a/tests/src/test/scala/fj/control/parallel/ArbitraryStrategy.scala +++ b/props-core-scalacheck/src/main/scala/fj/control/parallel/ArbitraryStrategy.scala @@ -2,9 +2,8 @@ package fj package control package parallel -import control._ import org.scalacheck.Arbitrary -import org.scalacheck.Gen.value +import org.scalacheck.Gen.const import Strategy.executorStrategy import java.util.concurrent.Executors.newFixedThreadPool import java.util.concurrent._ @@ -20,5 +19,5 @@ object ArbitraryStrategy { }) implicit def arbitraryStrategy[A]: Arbitrary[Strategy[A]] = - Arbitrary(value(executorStrategy[A](executor))) + Arbitrary(const(executorStrategy[A](executor))) } diff --git a/tests/src/test/scala/fj/data/ArbitraryArray.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryArray.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitraryArray.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryArray.scala diff --git a/tests/src/test/scala/fj/data/ArbitraryHashMap.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryHashMap.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitraryHashMap.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryHashMap.scala diff --git a/tests/src/test/scala/fj/data/ArbitraryHashSet.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryHashSet.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitraryHashSet.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryHashSet.scala diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala new file mode 100644 index 00000000..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/tests/src/test/scala/fj/data/ArbitraryList.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryList.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitraryList.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryList.scala diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala new file mode 100644 index 00000000..4210eef7 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala @@ -0,0 +1,13 @@ +package fj.data + +import org.scalacheck.Arbitrary + +/** + * A Scalacheck [[Arbitrary]] for [[Natural]]. + */ +object ArbitraryNatural { + + implicit def arbitraryNatural: Arbitrary[Natural] = + Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.abs).map(bi => Natural.natural(bi.bigInteger).some())) + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala new file mode 100644 index 00000000..2258ba9b --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala @@ -0,0 +1,16 @@ +package fj.data + +import fj.data.NonEmptyList.nel +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.{Arbitrary, Gen} + +/** + * A Scalacheck [[Arbitrary]] for [[NonEmptyList]]. + */ +object ArbitraryNonEmptyList { + implicit def arbitraryNonEmptyList[A](implicit a: Arbitrary[A]): Arbitrary[NonEmptyList[A]] = + Arbitrary(nelOf(arbitrary[A])) + + def nelOf[A](g: => Gen[A]): Gen[NonEmptyList[A]] = + Gen.nonEmptyListOf(g).map(l => l.tail.foldRight(nel(l.head))((x, n) => n.cons(x))) +} diff --git a/tests/src/test/scala/fj/data/ArbitraryOption.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryOption.scala similarity index 70% rename from tests/src/test/scala/fj/data/ArbitraryOption.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryOption.scala index 43bb08fc..d92a70f5 100644 --- a/tests/src/test/scala/fj/data/ArbitraryOption.scala +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryOption.scala @@ -3,10 +3,10 @@ package data import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary -import org.scalacheck.Gen.{value, sized, resize} +import org.scalacheck.Gen.{const, sized, resize} import Option.{none, some} object ArbitraryOption { implicit def arbitraryOption[A](implicit a: Arbitrary[A]): Arbitrary[Option[A]] = - Arbitrary(sized(n => if(n == 0) value(none[A]) else resize(n - 1, arbitrary[A]).map(some(_)))) + Arbitrary(sized(n => if(n == 0) const(none[A]) else resize(n - 1, arbitrary[A]).map(some(_)))) } diff --git a/tests/src/test/scala/fj/data/ArbitrarySet.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitrarySet.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitrarySet.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitrarySet.scala diff --git a/tests/src/test/scala/fj/data/ArbitraryStream.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryStream.scala similarity index 100% rename from tests/src/test/scala/fj/data/ArbitraryStream.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryStream.scala diff --git a/tests/src/test/scala/fj/data/ArbitraryTree.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryTree.scala similarity index 71% rename from tests/src/test/scala/fj/data/ArbitraryTree.scala rename to props-core-scalacheck/src/main/scala/fj/data/ArbitraryTree.scala index e8169f0c..1138e9d4 100644 --- a/tests/src/test/scala/fj/data/ArbitraryTree.scala +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryTree.scala @@ -8,13 +8,17 @@ import org.scalacheck.Gen import ArbitraryStream.arbitraryStream import ArbitraryList.{listOf} import Tree.{node, leaf} -import math.{round, sqrt} object ArbitraryTree { implicit def arbitraryTree[A](implicit a: Arbitrary[A]): Arbitrary[Tree[A]] = { def tree(implicit a:Arbitrary[A], n:Int, g:Gen[A]) : Gen[Tree[A]] = n match { case 0 => g.map(leaf(_)) - case n => choose(0, 10).flatMap((i) => lzy(g.map2(resize(i, listOf(tree(a, n/5, g)).map((x:fj.data.List[Tree[A]]) => P.p(x.toStream)))) (node(_, _)))) + case n => choose(0, 10).flatMap(i => + lzy(Gen.zip( + g, + resize(i, listOf(tree(a, n/5, g)).map((x:fj.data.List[Tree[A]]) => P.p(x.toStream))) + ).map(t => node(t._1, t._2))) + ) } Arbitrary(sized(tree(a, _, arbitrary[A]))) } diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/IsoLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/IsoLaws.scala new file mode 100644 index 00000000..a562bd75 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/IsoLaws.scala @@ -0,0 +1,32 @@ +package fj +package data.optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import P.p1 +import Function.identity + +object IsoLaws { + + def apply[S: Arbitrary, A: Arbitrary](iso: PIso[S, S, A, A])(implicit sEqual: Equal[S], aEqual: Equal[A]) = new Properties("Iso") { + + property("get and reverseGet forms an Isomorphism") = forAll { (s: S, a: A) => + sEqual.eq(iso.reverseGet(iso.get(s)), s) + aEqual.eq(iso.get(iso.reverseGet(a)), a) + } + + property("set is a weaker version of reverseGet") = forAll { (s: S, a: A) => + sEqual.eq(iso.set(a).f(s), iso.reverseGet(a)) + } + + property("modifyF with Id does not do anything") = forAll { s: S => + sEqual.eq(iso.modifyP1F(p1()).f(s)._1(), s) + } + + property("modify with id does not do anything") = forAll { s: S => + sEqual.eq(iso.modify(identity()).f(s), s) + } + + } + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/LensLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/LensLaws.scala new file mode 100644 index 00000000..a1da1789 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/LensLaws.scala @@ -0,0 +1,36 @@ +package fj +package data.optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import P.p1 +import Function.identity + +object LensLaws { + + def apply[S: Arbitrary, A: Arbitrary](lens: PLens[S, S, A, A])(implicit sEqual: Equal[S], aEqual: Equal[A]) = new Properties("Lens") { + + property("setting what you get does not do anything") = forAll { s: S => + sEqual.eq(lens.set(lens.get(s)).f(s), s) + } + + property("you get what you set") = forAll { (s: S, a: A) => + aEqual.eq(lens.get(lens.set(a).f(s)), a) + } + + /** calling set twice is the same as calling set once */ + property("set is idempotent") = forAll { (s: S, a: A) => + sEqual.eq(lens.set(a).f(lens.set(a).f(s)), lens.set(a).f(s)) + } + + property("modifyF with Id does not do anything") = forAll { s: S => + sEqual.eq(lens.modifyP1F(p1()).f(s)._1(), s) + } + + property("modify with id does not do anything") = forAll { s: S => + sEqual.eq(lens.modify(identity()).f(s), s) + } + + } + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/OptionalLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/OptionalLaws.scala new file mode 100644 index 00000000..094df990 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/OptionalLaws.scala @@ -0,0 +1,47 @@ +package fj +package data.optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import P.p1 +import Function.{identity, constant} +import Equal.optionEqual + +object OptionalLaws { + + def apply[S: Arbitrary, A: Arbitrary](optional: POptional[S, S, A, A])(implicit sEqual: Equal[S], aEqual: Equal[A]) = new Properties("Optional") { + + property("setting what you get does not do anything") = forAll { s: S => + sEqual.eq(optional.getOrModify(s).either(identity(), {a:A => optional.set(a).f(s)}), s) + } + + property("you get what you set") = forAll { (s: S, a: A) => + optionEqual(aEqual).eq(optional.getOption(optional.set(a).f(s)), optional.getOption(s).map(constant(a))) + } + + /** calling set twice is the same as calling set once */ + property("set is idempotent") = forAll { (s: S, a: A) => + sEqual.eq(optional.set(a).f(optional.set(a).f(s)), optional.set(a).f(s)) + } + + /** modifyF does not change the number of targets */ + property("modifyF with Id does not do anything") = forAll { s: S => + sEqual.eq(optional.modifyP1F(p1()).f(s)._1(), s) + } + + /** modify does not change the number of targets */ + property("modify with id does not do anything") = forAll { s: S => + sEqual.eq(optional.modify(identity()).f(s), s) + } + + property("setOption only succeeds when the Optional is matching") = forAll { (s: S, a: A) => + optionEqual(sEqual).eq(optional.setOption(a).f(s), optional.getOption(s).map(Function.constant(optional.set(a).f(s)))) + } + + property("modifyOption with id is isomorphomic to isMatching") = forAll { s: S => + optionEqual(sEqual).eq(optional.modifyOption(identity()).f(s), optional.getOption(s).map(constant(s))) + } + + } + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/PrismLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/PrismLaws.scala new file mode 100644 index 00000000..075825cb --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/PrismLaws.scala @@ -0,0 +1,44 @@ +package fj +package data +package optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import P.p1 +import Function.{identity, constant} +import Equal.optionEqual +import Option.some + +object PrismLaws { + + def apply[S: Arbitrary, A: Arbitrary](prism: PPrism[S, S, A, A])(implicit sEqual: Equal[S], aEqual: Equal[A]) = new Properties("Prism") { + + property("reverseGet produces a value") = forAll { a: A => + optionEqual(aEqual).eq(prism.getOption(prism.reverseGet(a)), some(a)) + } + + property("if a Prism match you can always go back to the source") = forAll { s: S => + sEqual.eq(prism.getOrModify(s).either(identity(), {a:A => prism.reverseGet(a)}), s) + } + + /** modifyF does not change the number of targets */ + property("modifyF with Id does not do anything") = forAll { s: S => + sEqual.eq(prism.modifyP1F(p1()).f(s)._1(), s) + } + + /** modify does not change the number of targets */ + property("modify with id does not do anything") = forAll { s: S => + sEqual.eq(prism.modify(identity()).f(s), s) + } + + property("setOption only succeeds when the prism is matching") = forAll { (s: S, a: A) => + optionEqual(sEqual).eq(prism.setOption(a).f(s), prism.getOption(s).map(constant(prism.set(a).f(s)))) + } + + property("modifyOption with id is isomorphomic to isMatching") = forAll { s: S => + optionEqual(sEqual).eq(prism.modifyOption(identity()).f(s), prism.getOption(s).map(constant(s))) + } + + } + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/SetterLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/SetterLaws.scala new file mode 100644 index 00000000..764698ef --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/SetterLaws.scala @@ -0,0 +1,25 @@ +package fj +package data +package optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import Function.identity + +object SetterLaws { + + def apply[S: Arbitrary, A: Arbitrary](setter: PSetter[S, S, A, A])(implicit sEqual: Equal[S]) = new Properties("Setter") { + + /** calling set twice is the same as calling set once */ + property("set is idempotent") = forAll { (s: S, a: A) => + sEqual.eq(setter.set(a).f(setter.set(a).f(s)), setter.set(a).f(s)) + } + + /** modify does not change the number of targets */ + property("modify preserves the structure") = forAll { s: S => + sEqual.eq(setter.modify(identity()).f(s), s) + } + + } + +} \ No newline at end of file diff --git a/props-core-scalacheck/src/main/scala/fj/data/optic/TraversalLaws.scala b/props-core-scalacheck/src/main/scala/fj/data/optic/TraversalLaws.scala new file mode 100644 index 00000000..423dac6a --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/optic/TraversalLaws.scala @@ -0,0 +1,42 @@ +package fj +package data +package optic + +import org.scalacheck.Prop._ +import org.scalacheck.{ Arbitrary, Properties } +import P.p1 +import Function.{identity, constant} +import Equal.{optionEqual, listEqual} +import Option.some + +object TraversalLaws { + + def apply[S: Arbitrary, A: Arbitrary](traversal: PTraversal[S, S, A, A])(implicit sEqual: Equal[S], aEqual: Equal[A]) = new Properties("Traversal") { + + /** set does not change the number of targets */ + property("you get what you set") = forAll { (s: S, a: A) => + listEqual(aEqual).eq(traversal.getAll(traversal.set(a).f(s)), traversal.getAll(s).map(constant(a))) + } + + /** calling set twice is the same as calling set once */ + property("set is idempotent") = forAll { (s: S, a: A) => + sEqual.eq(traversal.set(a).f(traversal.set(a).f(s)), traversal.set(a).f(s)) + } + + /** modifyF does not change the number of targets */ + property("modifyF preserves the structure") = forAll { s: S => + sEqual.eq(traversal.modifyP1F(p1()).f(s)._1(), s) + } + + /** modify does not change the number of targets */ + property("modify preserves the structure") = forAll { s: S => + sEqual.eq(traversal.modify(identity()).f(s), s) + } + + property("headMaybe returns the first element of getAll (if getAll is finite)") = forAll { s: S => + optionEqual(aEqual).eq(traversal.headOption(s), traversal.getAll(s).headOption()) + } + + } + +} diff --git a/props-core-scalacheck/src/main/scala/fj/package.scala b/props-core-scalacheck/src/main/scala/fj/package.scala new file mode 100644 index 00000000..78afe491 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/package.scala @@ -0,0 +1,26 @@ +import fj.Equal +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) + } + + implicit def Function2F[A, B, C](g: (A, B) => C): F[A, F[B, C]] = new F[A, F[B, C]] { + def f(a: A) = new F[B, C] { + def f(b: B) = g(a, b) + } + } + + implicit def stringEqual: Equal[String] = Equal.stringEqual + + 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/CheckP2.scala b/props-core-scalacheck/src/test/scala/fj/CheckP2.scala new file mode 100644 index 00000000..8aa7bdeb --- /dev/null +++ b/props-core-scalacheck/src/test/scala/fj/CheckP2.scala @@ -0,0 +1,21 @@ +package fj + +import java.lang +import org.scalacheck.Prop._ +import data.ArbitraryList._ +import ArbitraryP.arbitraryP2 +import ArbitraryUnit._ +import P.p +import Unit.unit +import org.scalacheck.Properties +import fj.data.optic.PrismLaws +import fj.data.optic.OptionalLaws +import fj.data.optic.LensLaws + +object CheckP2 extends Properties("P2") { + + property("Optic._1") = LensLaws[P2[String, Int], String](P2.Optic._1()) + + property("Optic._2") = LensLaws[P2[String, Int], Int](P2.Optic._2()) + +} 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/tests/src/test/scala/fj/FunctionalJavaJUnitTest.java b/props-core-scalacheck/src/test/scala/fj/FunctionalJavaJUnitTest.java similarity index 100% rename from tests/src/test/scala/fj/FunctionalJavaJUnitTest.java rename to props-core-scalacheck/src/test/scala/fj/FunctionalJavaJUnitTest.java diff --git a/tests/src/test/scala/fj/Implicit.scala b/props-core-scalacheck/src/test/scala/fj/Implicit.scala similarity index 100% rename from tests/src/test/scala/fj/Implicit.scala rename to props-core-scalacheck/src/test/scala/fj/Implicit.scala diff --git a/props-core-scalacheck/src/test/scala/fj/Tests.scala b/props-core-scalacheck/src/test/scala/fj/Tests.scala new file mode 100644 index 00000000..c5ec3977 --- /dev/null +++ b/props-core-scalacheck/src/test/scala/fj/Tests.scala @@ -0,0 +1,46 @@ +package fj + +object Tests { + def tests = List( + CheckP2.properties, + CheckSemigroup.properties, + fj.data.CheckArray.properties, + fj.data.CheckIO.properties, + fj.data.CheckIteratee.properties, + fj.data.CheckList.properties, + fj.data.CheckStream.properties, + fj.data.CheckOption.properties, + fj.data.CheckTree.properties, + fj.data.CheckHashMap.properties, + fj.data.CheckHashSet.properties, + fj.data.CheckSet.properties, + fj.control.parallel.CheckStrategy.properties, + fj.control.parallel.CheckParModule.properties + ).flatten + + def main(args: Array[String]): scala.Unit = { + run(tests) + // System.exit(0) + } + + 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) + } + } + } + } +} diff --git a/tests/src/test/scala/fj/control/parallel/CheckParModule.scala b/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala similarity index 80% rename from tests/src/test/scala/fj/control/parallel/CheckParModule.scala rename to props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala index 8e13eb1b..bba1feff 100644 --- a/tests/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/tests/src/test/scala/fj/control/parallel/CheckStrategy.scala b/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckStrategy.scala similarity index 100% rename from tests/src/test/scala/fj/control/parallel/CheckStrategy.scala rename to props-core-scalacheck/src/test/scala/fj/control/parallel/CheckStrategy.scala diff --git a/tests/src/test/scala/fj/data/CheckArray.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala similarity index 90% rename from tests/src/test/scala/fj/data/CheckArray.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala index a1c8a446..b116a6a9 100755 --- a/tests/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() @@ -107,9 +107,11 @@ object CheckArray extends Properties("Array") { a.reverse.reverse, a)) - property("reverse") = forAll((a: Array[String], n: Int) => - (n >= 0 && n < a.length) ==> - (a.reverse.get(n) == a.get(a.length - 1 - n))) + property("reverse") = forAll((a: Array[String], x: Byte) => + (a.length > 0) ==> { + val n = math.abs(x) % a.length + (a.reverse.get(n) == a.get(a.length - 1 - n)) + }) property("appendLeftIdentity") = forAll((a: Array[String]) => arrayEqual(stringEqual).eq(a.append(empty[String]), a)) @@ -123,9 +125,11 @@ object CheckArray extends Properties("Array") { property("appendLength") = forAll((a: Array[String], b: Array[String]) => a.append(b).length == a.length + b.length) - property("array") = forAll((a: scala.Array[String], n: Int) => - (n >= 0 && n < a.length) ==> - (array[String](a: _*).length == a.length && array[String](a: _*).get(n) == a(n))) + property("array") = forAll((a: scala.Array[String], x: Byte) => + (a.length > 0) ==> { + val n = math.abs(x) % a.length + array[String](a: _*).length == a.length && array[String](a: _*).get(n) == a(n) + }) property("join") = forAll((a: Array[Array[String]]) => arrayEqual(stringEqual).eq( diff --git a/tests/src/test/scala/fj/data/CheckHashMap.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala similarity index 84% rename from tests/src/test/scala/fj/data/CheckHashMap.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala index ecf09174..c629a10f 100755 --- a/tests/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 comap ((x: Int) => (x: java.lang.Integer)) - implicit val hashInt: Hash[Int] = intHash comap ((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.from[Int, String](entries, equalInt, hashInt) - 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/tests/src/test/scala/fj/data/CheckHashSet.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckHashSet.scala similarity index 82% rename from tests/src/test/scala/fj/data/CheckHashSet.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckHashSet.scala index 5c711aea..d774ff74 100644 --- a/tests/src/test/scala/fj/data/CheckHashSet.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckHashSet.scala @@ -8,8 +8,8 @@ import Hash.intHash import org.scalacheck.Properties object CheckHashSet extends Properties("List") { - implicit val equalInt: Equal[Int] = intEqual comap ((x: Int) => (x: java.lang.Integer)) - implicit val hashInt: Hash[Int] = intHash comap ((x: Int) => (x: java.lang.Integer)) + 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)) property("eq") = forAll((s: HashSet[Int], x: Int, y: Int) => s.eq(x, y) == equalInt.eq(x, y)) diff --git a/tests/src/test/scala/fj/data/CheckIO.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckIO.scala similarity index 100% rename from tests/src/test/scala/fj/data/CheckIO.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckIO.scala diff --git a/tests/src/test/scala/fj/data/CheckIteratee.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala similarity index 79% rename from tests/src/test/scala/fj/data/CheckIteratee.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala index 76213a5d..ff085f87 100644 --- a/tests/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._ @@ -25,13 +24,12 @@ object CheckIteratee extends Properties("Iteratee") { enumerate[Int, java.lang.Integer](a, IterV.length[Int]).run == a.length) property("head") = forAll((a: List[Int]) => - enumerate(a, IterV.head[Int]).run == a.toOption) + enumerate(a, IterV.head[Int]).run == a.headOption) - property("drop") = forAll((a: List[String], n: Int) => { - (n >= 0 && n <= a.length) ==> { - val actual = enumerate(a, IterV.drop(n).bind(Function.constant(IterV.list[String]))).run.reverse - listEqual(stringEqual).eq(actual, a.drop(n)) - } + property("drop") = forAll((a: List[String], x: Byte) => (a.length > 0) ==> { + val n = math.abs(x) % a.length + val actual = enumerate(a, IterV.drop(n).bind(Function.constant(IterV.list[String]))).run.reverse + listEqual(stringEqual).eq(actual, a.drop(n)) }) property("list") = forAll((a: List[String]) => @@ -51,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/tests/src/test/scala/fj/data/CheckList.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckList.scala similarity index 72% rename from tests/src/test/scala/fj/data/CheckList.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckList.scala index 807e1293..d675db81 100644 --- a/tests/src/test/scala/fj/data/CheckList.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckList.scala @@ -1,14 +1,21 @@ package fj package data +import java.lang +import fj.Monoid import org.scalacheck.Prop._ -import ArbitraryList.arbitraryList -import ArbitraryP.arbitraryP1 +import ArbitraryList._ +import ArbitraryP._ +import ArbitraryUnit._ import Equal.{listEqual, stringEqual, p2Equal} import P.p import Unit.unit import List.{nil, single, join, iterateWhile} import org.scalacheck.Properties +import fj.data.optic.PrismLaws +import fj.data.optic.OptionalLaws +import fj.data.optic.TraversalLaws +import fj.data.optic.Traversal object CheckList extends Properties("List") { property("isEmpty") = forAll((a: List[Int]) => @@ -26,7 +33,7 @@ object CheckList extends Properties("List") { (listEqual(stringEqual).eq(a.orTail(n), a.tail))) property("toOption") = forAll((a: List[Int]) => - a.toOption.isNone || a.toOption.some == a.head) + a.headOption.isNone || a.headOption.some == a.head) // crashes the type checker for unknown reason // property("toEither") = property((a: List[Int], n: P1[Int]) => @@ -115,9 +122,11 @@ object CheckList extends Properties("List") { (a append b).reverse, b.reverse.append(a.reverse))) - property("index") = forAll((a: List[String], n: Int) => - (n > 0 && n < a.length) ==> - (a.index(n) == a.tail.index(n - 1))) + property("index") = forAll((a: List[String], x: Byte) => + (a.length > 0) ==> { + val n = math.abs(x) % a.length + 1 + (n < a.length) ==> (a.index(n) == a.tail.index(n - 1)) + }) property("snoc") = forAll((a: List[String], s: String) => listEqual(stringEqual).eq( @@ -169,11 +178,45 @@ object CheckList extends Properties("List") { property("nub") = forAll((a: List[String], b: List[String]) => listEqual(stringEqual).eq(a append b nub, a.nub.append(b.nub).nub)) + property("init") = forAll((a: List[String], b: String) => + listEqual(stringEqual).eq(a.snoc(b).init(), a)) + property("join") = forAll((a: List[List[String]]) => listEqual(stringEqual).eq( a.foldRight((a: List[String], b: List[String]) => a.append(b), nil[String]), join(a))) + property("groupBy") = forAll((a: List[Int]) => { + 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() + }) + + property("groupByMonoid") = forAll((a: List[Int]) => { + val result = a.groupBy((x: Int) => (x % 2 == 0): lang.Boolean, (x: Int) => x: lang.Integer, Monoid.intAdditionMonoid, Ord.booleanOrd) + result.get(true).forall((x: lang.Integer) => + x == a.filter((x: Int) => (x % 2 == 0): lang.Boolean). + map((x: Int) => x:lang.Integer). + foldLeft(Function.uncurryF2[lang.Integer, lang.Integer, lang.Integer](Monoid.intAdditionMonoid.sum), Monoid.intAdditionMonoid.zero()): lang.Boolean) && + result.get(false).forall((x: lang.Integer) => + x == a.filter((x: Int) => (x % 2 != 0): lang.Boolean). + map((x: Int) => x:lang.Integer). + foldLeft(Function.uncurryF2[lang.Integer, lang.Integer, lang.Integer](Monoid.intAdditionMonoid.sum), Monoid.intAdditionMonoid.zero()): lang.Boolean) + }) + + + property("Optic.nil") = PrismLaws[List[String], Unit](List.Optic.nil()) + + property("Optic.cons") = PrismLaws[List[String], P2[String, List[String]]](List.Optic.cons()) + + property("Optic.head") = OptionalLaws[List[String], String](List.Optic.head()) + + property("Optic.tail") = OptionalLaws[List[String], List[String]](List.Optic.tail()) + + property("Optic.traversal") = TraversalLaws[List[String], String](List.Optic.traversal()) + + /*property("iterateWhile") = forAll((n: Int) => n > 0 ==> (iterateWhile(((x:Int) => x - 1), ((x:Int) => ((x > 0): java.lang.Boolean)), n).length == n))*/ } diff --git a/tests/src/test/scala/fj/data/CheckOption.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala similarity index 90% rename from tests/src/test/scala/fj/data/CheckOption.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckOption.scala index 18d2cdf2..13059d3f 100644 --- a/tests/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/tests/src/test/scala/fj/data/CheckSet.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckSet.scala similarity index 97% rename from tests/src/test/scala/fj/data/CheckSet.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckSet.scala index af90480a..96bce311 100644 --- a/tests/src/test/scala/fj/data/CheckSet.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckSet.scala @@ -16,7 +16,7 @@ import org.scalacheck.Properties object CheckSet extends Properties("Set") { def idInt(n: Int) = n:java.lang.Integer - implicit def oi : Ord[Int] = intOrd.comap(idInt _) + implicit def oi : Ord[Int] = intOrd.contramap(idInt _) implicit def os : Ord[String] = stringOrd property("isEmpty") = forAll((a: Set[Int]) => diff --git a/tests/src/test/scala/fj/data/CheckStream.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckStream.scala similarity index 90% rename from tests/src/test/scala/fj/data/CheckStream.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckStream.scala index 26ac6d31..3a272953 100644 --- a/tests/src/test/scala/fj/data/CheckStream.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckStream.scala @@ -51,13 +51,15 @@ object CheckStream extends Properties("Stream") { val length = Gen.choose(0, 5000) property("bindStackOverflow") = forAll(length)(size => { - val stream = iterableStream(JavaConversions.asJavaIterable((1 to size))) - val bound: Stream[Int] = stream.bind(new F[Int, Stream[Int]] { - def f(a: Int) = single(a) + val stream = Stream.range(1, size + 1) + val bound: Stream[Integer] = stream.bind(new F[Integer, Stream[Integer]] { + def f(a: Integer) = single(a) }) - stream.zip(bound).forall(new F[P2[Int, Int], java.lang.Boolean] { - def f(a: P2[Int, Int]) = a._1() == a._2() + val zipped = stream.zip(bound) + val allmatch = zipped.forall(new F[P2[Integer, Integer], java.lang.Boolean] { + def f(a: P2[Integer, Integer]) = a._1() == a._2() }) + allmatch && zipped.length() == size }) property("foreach") = forAll((a: Stream[Int]) => { @@ -128,9 +130,11 @@ object CheckStream extends Properties("Stream") { (a append b).reverse, b.reverse.append(a.reverse))) - property("index") = forAll((a: Stream[String], n: Int) => - (n > 0 && n < a.length) ==> - (a.index(n) == a.tail._1.index(n - 1))) + property("index") = forAll((a: Stream[String], x: Byte) => + (a.length > 0) ==> { + val n = math.abs(x) % a.length + 1 + (n < a.length) ==> (a.index(n) == a.tail._1.index(n - 1)) + }) property("forall") = forAll((a: Stream[Int]) => a.forall((x: Int) => ((x % 2 == 0): java.lang.Boolean)) == diff --git a/tests/src/test/scala/fj/data/CheckTree.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckTree.scala similarity index 100% rename from tests/src/test/scala/fj/data/CheckTree.scala rename to props-core-scalacheck/src/test/scala/fj/data/CheckTree.scala diff --git a/props-core/build.gradle b/props-core/build.gradle new file mode 100644 index 00000000..534a051b --- /dev/null +++ b/props-core/build.gradle @@ -0,0 +1,8 @@ + +archivesBaseName = "${project.projectName}-${project.name}" + +dependencies { + 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 new file mode 100644 index 00000000..605546ef --- /dev/null +++ b/props-core/src/test/java/fj/MemoisationTest.java @@ -0,0 +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; + +@RunWith(PropertyTestRunner.class) +public class MemoisationTest { + + 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))); + }); + } + + 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))); + }); + } + + @Test + 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 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()); + }); + } + + @Test + 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()); + }); + } + + @Test + 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()); + }); + } + + @Test + 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()); + }); + } + + @Test + 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()); + }); + } + + @Test + 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()); + }); + } + +} 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 new file mode 100644 index 00000000..48352620 --- /dev/null +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -0,0 +1,126 @@ +package fj.data; + +import fj.F; +import fj.data.test.PropertyAssert; +import fj.test.*; +import org.junit.Test; + +import static fj.test.Arbitrary.*; +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; + + +public class ReaderTest { + + @Test + public void testMap() { + // (3 + 8) * 11 + // 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); + } + + @Test + public void testFlatMap() { + // (3 * 2) + (3 + 10) + // example taken from http://learnyouahaskell.com/for-a-few-monads-more + int y = Reader.unit((Integer i) -> i * 2).flatMap(a -> Reader.unit((Integer i) -> i + 10).map(b -> a + b)).f(3); +// System.out.println(y); // 19 + assertTrue(y == 19); + } + + @Test + public void testMapProp() { + Property p = property( + arbF(cogenInteger, arbInteger), + arbF(cogenInteger, arbInteger), + arbInteger, + (f, g, 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)); + }); + PropertyAssert.assertResult(p); + } + + @Test + public void testFlatMapProp() { + Gen>> a = arbF(cogenInteger, arbReader()); + Property p = property( + arbF(cogenInteger, arbInteger), + a, + arbInteger, + (f, g, 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)); + } + ); + PropertyAssert.assertResult(p); + } + + // Left identity: return a >>= f == f a + @Test + public void testLeftIdentity() { + Property p = Property.property( + arbInteger, + arbInteger, + arbF(cogenInteger, arbReader()), + (i, j, f) -> { + int a = Reader.constant(i).flatMap(f).f(j); + int b = f.f(i).f(j); + return prop(a == b); + }); + PropertyAssert.assertResult(p); + } + + // Right identity: m >>= return == m + @Test + public void testRightIdentity() { + Property p = Property.property( + arbInteger, + arbReader(), + (i, r2) -> { + return prop(r2.flatMap(a -> Reader.constant(a)).f(i) == r2.f(i)); + }); + PropertyAssert.assertResult(p); + } + + // Associativity: (m >>= f) >>= g == m >>= (\x -> f x >>= g) + @Test + public void testAssociativity() { + Property p = Property.property( + arbInteger, + 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); + }); + PropertyAssert.assertResult(p); + } + + @Test + public void testAndThen() { + final int y = Reader.unit((Integer i) -> i * 2).andThen(i -> i + 10).f(10); + assertThat(y, is(30)); + } + + @Test + public void testBind() { + final int y = Reader.unit((Integer i) -> i * 2) + .bind(a -> Reader.unit(i -> a + i + 11)).f(10); + assertThat(y, is(41)); + } + + public Gen> arbReader() { + return Arbitrary.arbReader(cogenInteger, arbInteger); + } + + +} diff --git a/props-core/src/test/java/fj/data/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 new file mode 100644 index 00000000..41143c17 --- /dev/null +++ b/props-core/src/test/java/fj/data/TestRngState.java @@ -0,0 +1,147 @@ +package fj.data; + +import fj.*; +import fj.test.Arbitrary; +import fj.test.Cogen; +import fj.test.Gen; +import fj.test.Property; +import org.junit.Assert; +import org.junit.Test; + +import static fj.data.Option.some; +import static fj.data.Stream.unfold; +import static fj.data.test.PropertyAssert.assertResult; +import static fj.test.Arbitrary.*; +import static fj.test.Cogen.cogenInteger; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static fj.test.Variant.variant; + +public class TestRngState { + + static List expected1 = List.list(4,4,2,2,2,5,3,3,1,5); + static int size = 10; + static final Equal> listIntEqual = Equal.listEqual(Equal.intEqual); + + static Rng defaultRng() { + return new LcgRng(1); + } + + static P2 num(Rng r) { + return r.range(1, 5); + } + + static State defaultState() { + return State.unit(s -> num(s)); + } + + static F, State> nextState() { + return s -> s.mapState(p2 -> num(p2._1())); + } + + static P2 num(Rng r, int x) { + return r.range(x, x + 1); + } + + @Test + public void testUnfold() { + Stream s = unfold(r -> some(num(r).swap()), defaultRng()); + Assert.assertTrue(listIntEqual.eq(s.take(size).toList(), expected1)); + } + + @Test + public void testTransitions() { + P2>, State> p = List.replicate(size, nextState()).foldLeft( + (P2>, State> p2, F, State> f) -> { + State s = f.f(p2._2()); + return P.p(p2._1().snoc(p2._2()), s); + } + , P.p(List.nil(), defaultState()) + ); + List ints = p._1().map(s -> s.eval(defaultRng())); + Assert.assertTrue(listIntEqual.eq(ints, expected1)); + } + + @Test + public void testSequence() { + List list = State.sequence(List.replicate(size, defaultState())).eval(defaultRng()); + Assert.assertTrue(listIntEqual.eq(list, expected1)); + } + + @Test + public void testTraverse() { + List list = State.traverse(List.range(1, 10), a -> (State.unit((Rng s) -> num(s, a)))).eval(defaultRng()); +// System.out.println(list.toString()); + List expected = List.list(1,2,3,5,6,7,7,9,10); + Assert.assertTrue(listIntEqual.eq(list, expected)); + } + + public static Gen> arbState() { + return Arbitrary.arbState(Arbitrary.arbLcgRng(), Cogen.cogenLcgRng(), arbInteger); + } + + public static Gen>> arbStateF() { + return arbF(Cogen.cogenLcgRng(), arbP2(arbLcgRng(), arbInteger)); + } + + public static Cogen> cogenState() { + return Cogen.cogenState(Arbitrary.arbLcgRng(), (LcgRng s, Integer j) -> (long) (j >= 0 ? 2 * j : -2 * j + 1)); + } + + public static Gen>> arbBindable() { + return arbF(cogenInteger, arbState()); + } + + // Left identity: return i >>= f == f i + @Test + public void testLeftIdentity() { + Property p = property( + arbBindable(), + arbInteger, + arbLcgRng(), + (f, i, r) -> { + int a = State.constant(i).flatMap(f).eval(r); + int b = f.f(i).eval(r); +// System.out.println(String.format("a=%d, b=%d", a, b)); + return prop(a == b); + } + ); + assertResult(p); + } + + + // Right identity: m >>= return == m + @Test + public void testRightIdentity() { + Property p = Property.property( + arbState(), + arbLcgRng(), + (s, r) -> { + int x = s.flatMap(a -> State.constant(a)).eval(r); + int y = s.eval(r); +// System.out.println(String.format("x=%d, y=%d", x, y)); + return prop(x == y); + } + ); + assertResult(p); + } + + // Associativity: (m >>= f) >>= g == m >>= (\x -> f x >>= g) + @Test + public void testAssociativity() { + Property p = Property.property( + arbState(), + arbBindable(), + arbBindable(), + arbLcgRng(), + (s, f, g, r) -> { + int t = s.flatMap(f).flatMap(g).eval(r); + int u = s.flatMap(x -> f.f(x).flatMap(g)).eval(r); +// System.out.println(String.format("x=%d, y=%d", t, u)); + return prop(t == u); + }); + assertResult(p); + } + + +} diff --git a/props-core/src/test/java/fj/data/WriterTest.java b/props-core/src/test/java/fj/data/WriterTest.java new file mode 100644 index 00000000..50134da3 --- /dev/null +++ b/props-core/src/test/java/fj/data/WriterTest.java @@ -0,0 +1,116 @@ +package fj.data; + +import fj.Equal; +import fj.F; +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.Cogen.cogenInteger; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class WriterTest { + + @Test + public void base() { + Assert.assertTrue(tellTruth("a", "b", 0)); + } + + boolean tellTruth(String s1, String s2, int i) { + Writer w = defaultWriter.f(i); + Writer w1 = w.tell(s1).tell(s2); + Writer w2 = w.tell(w.monoid().sum(s1, s2)); + boolean b = eq.eq(w1, w2); +// System.out.println(String.format("p1: %s, p2: %s, b: %s", w1, w2, b)); + return b; + } + + final Equal> eq = Equal.writerEqual(Equal.stringEqual, Equal.intEqual); + final F> defaultWriter = Writer.stringLogger(); + + @Test + public void testTellProp() { + Property p = property(arbString, arbString, arbInteger, (s1, s2, i) -> prop(tellTruth(s1, s2, i))); + assertResult(p); + } + + @Test + public void testMap() { + 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); + }); + assertResult(p); + } + + @Test + public void testFlatMap() { + 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); + }); + assertResult(p); + + } + + public Gen> arbWriterStringInt() { + return arbWriterString(arbInteger); + } + + public Gen> arbWriterString(Gen arb) { + return arb.map(a -> Writer.stringLogger().f(a)); + } + + // Left identity: return a >>= f == f a + @Test + public void testLeftIdentity() { + Property p = Property.property( + arbInteger, + arbF(cogenInteger, arbWriterStringInt()), + (i, f) -> { + return prop(eq.eq(defaultWriter.f(i).flatMap(f), f.f(i))); + }); + assertResult(p); + } + + // Right identity: m >>= return == m + @Test + public void testRightIdentity() { + Property p = Property.property( + arbWriterStringInt(), + (w) -> prop(eq.eq(w.flatMap(a -> defaultWriter.f(a)), w)) + ); + assertResult(p); + } + + // Associativity: (m >>= f) >>= g == m >>= (\x -> f x >>= g) + @Test + public void testAssociativity() { + Property p = Property.property( + 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); + }); + 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 new file mode 100644 index 00000000..446716c2 --- /dev/null +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeProperties.java @@ -0,0 +1,32 @@ +package fj.data.fingertrees; + +import fj.data.Stream; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbList; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class FingerTreeProperties { + + 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 new file mode 100644 index 00000000..8cc94825 --- /dev/null +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -0,0 +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.Monoid.intAdditionMonoid; +import static fj.Monoid.intMinMonoid; +import static fj.data.fingertrees.FingerTree.measured; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class FingerTreeTest { + + public static final int SIZE = 10; + + @Test + public void size() { + 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 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 new file mode 100644 index 00000000..feb5dcf4 --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/ArrayProperties.java @@ -0,0 +1,208 @@ +package fj.data.properties; + +import fj.*; +import fj.data.Array; +import fj.data.Either; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static fj.Equal.arrayEqual; +import static fj.Equal.intEqual; +import static fj.Function.compose; +import static fj.Function.identity; +import static fj.data.Array.array; +import static fj.data.Array.empty; +import static fj.test.Arbitrary.*; +import static fj.test.Property.implies; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class ArrayProperties { + + private static final Equal> eq = arrayEqual(intEqual); + + 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))); + + public Property isEmpty() { + return property(arbArray(arbInteger), array -> prop(array.isEmpty() != array.isNotEmpty())); + } + + public Property isNotEmpty() { + return property(arbArray(arbInteger), array -> prop(array.length() > 0 == array.isNotEmpty())); + } + + public Property toOption() { + return property(arbArray(arbInteger), array -> + prop(array.toOption().isNone() || intEqual.eq(array.toOption().some(), array.get(0)))); + } + + public Property toEither() { + return property(arbArray(arbInteger), arbP1(arbInteger), (array, n) -> { + final Either e = array.toEither(n); + return prop(e.isLeft() && intEqual.eq(e.left().value(), n._1()) || + intEqual.eq(e.right().value(), array.get(0))); + }); + } + + public Property mapId() { + return property(arbArray(arbInteger), array -> prop(eq.eq(array.map(identity()), array))); + } + + public Property mapCompose() { + final F f = x -> x + 3; + final F g = x -> x * 4; + return property(arbArray(arbInteger), array -> + prop(eq.eq(array.map(compose(f, g)), array.map(g).map(f)))); + } + + public Property foreachDoEffect() { + return property(arbArray(arbInteger), array -> { + int[] acc = {0}; + array.foreachDoEffect(x -> acc[0] += x); + + int acc2 = 0; + for (int x : array) { acc2 += x; } + + return prop(intEqual.eq(acc[0], acc2)); + }); + } + + public Property filter() { + final F predicate = (x -> x % 2 == 0); + return property(arbArray(arbInteger), array -> prop(array.filter(predicate).forall(predicate))); + } + + public Property filterLength() { + final F predicate = (x -> x % 2 == 0); + return property(arbArray(arbInteger), array -> prop(array.filter(predicate).length() <= array.length())); + } + + public Property bindLeftIdentity() { + final F> f = (i -> array(-i)); + return property(arbArray(arbInteger), arbInteger, (array, i) -> + prop(eq.eq(array(i).bind(f), f.f(i)))); + } + + public Property bindRightIdentity() { + return property(arbArray(arbInteger), array -> prop(eq.eq(array.bind(Array::array), array))); + } + + public Property bindAssociativity() { + final F> f = x -> array(x + 3); + final F> g = x -> array(x * 4); + return property(arbArray(arbInteger), array -> + prop(eq.eq(array.bind(f).bind(g), array.bind(i -> f.f(i).bind(g))))); + } + + public Property foldRight() { + return property(arbArray(arbInteger), array -> + prop(eq.eq(array.foldRight((i, s) -> array(i).append(s), empty()), array))); + } + + public Property foldLeft() { + return property(arbArray(arbInteger), array -> + prop(eq.eq(array.foldLeft((s, i) -> array(i).append(s), empty()), + array.reverse().foldRight((i, s) -> array(i).append(s), empty())))); + } + + public Property scans() { + return property(arbArray(arbInteger), arbInteger, (array, z) -> { + final F> add = x -> y -> x + y; + final Array left = array.scanLeft(add, z); + final Array right = array.reverse().scanRight(add, z).reverse(); + return prop(eq.eq(left, right)); + }); + } + + public Property scans1() { + return property(arbArray(arbInteger), array -> + implies(array.isNotEmpty(), () -> { + final F> add = x -> y -> x + y; + final Array left = array.scanLeft1(add); + final Array right = array.reverse().scanRight1(add).reverse(); + return prop(eq.eq(left, right)); + })); + } + + @CheckParams(maxSize = 100) + public Property sequence() { + return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> + prop(eq.eq(array1.sequence(array2), array1.bind(__ -> array2)))); + } + + public Property reverseIdentity() { + return property(arbArray(arbInteger), array -> + prop(eq.eq(array.reverse().reverse(), array))); + } + + public Property reverse() { + return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> + prop(eq.eq(array1.append(array2).reverse(), array2.reverse().append(array1.reverse())))); + } + + @CheckParams(minSize = 1) + public Property reverseIndex() { + return property(arbArrayWithIndex, p -> { + final Array array = p._1(); + final Integer i = p._2(); + return prop(intEqual.eq(array.reverse().get(i), array.get(array.length() - i - 1))); + }); + } + + public Property appendLeftIdentity() { + return property(arbArray(arbInteger), array -> prop(eq.eq(Array. empty().append(array), array))); + } + + public Property appendRightIdentity() { + return property(arbArray(arbInteger), array -> prop(eq.eq(array.append(empty()), array))); + } + + public Property appendAssociativity() { + return property(arbArray(arbInteger), arbArray(arbInteger), arbArray(arbInteger), (array1, array2, array3) -> + prop(eq.eq(array1.append(array2).append(array3), array1.append(array2.append(array3))))); + } + + public Property appendLength() { + return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> + prop(array1.append(array2).length() == array1.length() + array2.length())); + } + + public Property arrayLength() { + return property(arbArray(arbInteger), array -> prop(array.length() == array.array().length)); + } + + @CheckParams(minSize = 1) + public Property index() { + return property(arbArrayWithIndex, p -> { + final Array array = p._1(); + final Integer i = p._2(); + return prop(intEqual.eq(array.get(i), array.array(Integer[].class)[i])); + }); + } + + public Property forallExists() { + return property(arbArray(arbInteger), array -> + prop(array.forall(x -> x % 2 == 0) == !array.exists(x -> x % 2 != 0))); + } + + public Property find() { + return property(arbArray(arbInteger), array -> prop(array.find(x -> x % 2 == 0).forall(x -> x % 2 == 0))); + } + + @CheckParams(maxSize = 500) + public Property join() { + return property(arbArray(arbArray(arbInteger)), (Array> array) -> + prop(eq.eq(array.foldRight(Array::append, empty()), Array.join(array)))); + } +} diff --git a/props-core/src/test/java/fj/data/properties/ListProperties.java b/props-core/src/test/java/fj/data/properties/ListProperties.java new file mode 100644 index 00000000..fad5f66f --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/ListProperties.java @@ -0,0 +1,322 @@ +package fj.data.properties; + +import fj.*; +import fj.data.List; +import fj.data.Stream; +import fj.data.TreeMap; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import fj.test.Gen; +import fj.test.Property; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collections; + +import static fj.Equal.listEqual; +import static fj.Equal.p2Equal; +import static fj.Function.compose; +import static fj.Function.identity; +import static fj.P.p; +import static fj.data.List.nil; +import static fj.data.List.single; +import static fj.test.Arbitrary.*; +import static fj.test.Property.implies; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static fj.Equal.intEqual; +import static fj.Monoid.intAdditionMonoid; +import static fj.Ord.booleanOrd; +import static fj.Ord.intOrd; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class ListProperties { + + private static final Equal> eq = listEqual(intEqual); + + private static final Gen, Integer>> arbListWithIndex = arbList(arbInteger) + .filter(List::isNotEmpty) + .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())); + } + + public Property isNotEmpty() { + return property(arbList(arbInteger), list -> prop(list.length() > 0 == list.isNotEmpty())); + } + + public Property orHead() { + return property(arbList(arbInteger), arbInteger, (list, n) -> + implies(list.isNotEmpty(), () -> prop(intEqual.eq(list.orHead(() -> n), list.head())))); + } + + public Property orTail() { + return property(arbList(arbInteger), arbP1(arbList(arbInteger)), (list, list2) -> + implies(list.isNotEmpty(), () -> prop(eq.eq(list.orTail(list2), list.tail())))); + } + + public Property toOption() { + return property(arbList(arbInteger), list -> + prop(list.headOption().isNone() || intEqual.eq(list.headOption().some(), list.head()))); + } + + public Property consHead() { + return property(arbList(arbInteger), arbInteger, (list, n) -> prop(intEqual.eq(list.cons(n).head(), n))); + } + + public Property consLength() { + return property(arbList(arbInteger), arbInteger, (list, n) -> prop(list.cons(n).length() == list.length() + 1)); + } + + public Property mapId() { + return property(arbList(arbInteger), list -> prop(eq.eq(list.map(identity()), list))); + } + + public Property mapCompose() { + final F f = x -> x + 3; + final F g = x -> x * 4; + return property(arbList(arbInteger), list -> + prop(eq.eq(list.map(compose(f, g)), list.map(g).map(f)))); + } + + public Property foreachDoEffect() { + return property(arbList(arbInteger), list -> { + int[] acc = {0}; + list.foreachDoEffect(x -> acc[0] += x); + + int acc2 = 0; + for (int x : list) { acc2 += x; } + + return prop(intEqual.eq(acc[0], acc2)); + }); + } + + public Property filter() { + final F predicate = (x -> x % 2 == 0); + return property(arbList(arbInteger), list -> prop(list.filter(predicate).forall(predicate))); + } + + public Property filterLength() { + final F predicate = (x -> x % 2 == 0); + return property(arbList(arbInteger), list -> prop(list.filter(predicate).length() <= list.length())); + } + + public Property bindLeftIdentity() { + final F> f = (i -> single(-i)); + return property(arbList(arbInteger), arbInteger, (list, i) -> + prop(eq.eq(single(i).bind(f), f.f(i)))); + } + + public Property bindRightIdentity() { + return property(arbList(arbInteger), list -> prop(eq.eq(list.bind(List::list), list))); + } + + public Property bindAssociativity() { + final F> f = x -> single(x + 3); + final F> g = x -> single(x * 4); + return property(arbList(arbInteger), list -> + prop(eq.eq(list.bind(f).bind(g), list.bind(i -> f.f(i).bind(g))))); + } + + public Property foldRight() { + return property(arbList(arbInteger), list -> + prop(eq.eq(list.foldRight((i, s) -> single(i).append(s), nil()), list))); + } + + public Property foldLeft() { + return property(arbList(arbInteger), list -> + prop(eq.eq(list.foldLeft((s, i) -> single(i).append(s), nil()), + list.reverse().foldRight((i, s) -> single(i).append(s), nil())))); + } + + public Property tailLength() { + return property(arbList(arbInteger), list -> + implies(list.isNotEmpty(), () -> prop(list.tail().length() == list.length() - 1))); + } + + public Property reverseIdentity() { + return property(arbList(arbInteger), list -> prop(eq.eq(list.reverse().reverse(), list))); + } + + public Property reverse() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + prop(eq.eq(list1.append(list2).reverse(), list2.reverse().append(list1.reverse())))); + } + + @CheckParams(maxSize = 100) + public Property sequence() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + prop(eq.eq(list1.sequence(list2), list1.bind(__ -> list2)))); + } + + public Property appendLeftIdentity() { + return property(arbList(arbInteger), list -> prop(eq.eq(List. nil().append(list), list))); + } + + public Property appendRightIdentity() { + return property(arbList(arbInteger), list -> prop(eq.eq(list.append(nil()), list))); + } + + public Property appendAssociativity() { + return property(arbList(arbInteger), arbList(arbInteger), arbList(arbInteger), (list1, list2, list3) -> + prop(eq.eq(list1.append(list2).append(list3), list1.append(list2.append(list3))))); + } + + public Property appendLength() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + prop(list1.append(list2).length() == list1.length() + list2.length())); + } + + @CheckParams(minSize = 2, maxSize = 10000) + public Property indexTail() { + 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(gen, pair -> { + final List list = pair._1(); + final int i = pair._2(); + return prop(intEqual.eq(list.index(i), list.tail().index(i - 1))); + }); + } + + public Property snoc() { + return property(arbList(arbInteger), arbInteger, (list, n) -> prop(eq.eq(list.snoc(n), list.append(single(n))))); + } + + public Property take() { + return property(arbList(arbInteger), arbInteger, (list, n) -> prop(list.take(n).length() <= list.length())); + } + + public Property drop() { + return property(arbList(arbInteger), arbInteger, (list, n) -> prop(list.drop(n).length() <= list.length())); + } + + public Property splitAt() { + return property(arbList(arbInteger), arbInteger, (list, n) -> + prop(p2Equal(eq, eq).eq(list.splitAt(n), p(list.take(n), list.drop(n))))); + } + + @CheckParams(minSize = 1, maxSize = 2000) + public Property partition() { + return property(arbListWithIndex, p -> implies(p._2() > 0, () -> { + final List list = p._1(); + final Integer i = p._2(); + final List> partition = list.partition(i); + return prop(eq.eq(list, List.join(partition))).and(prop(partition.forall(part -> part.length() <= i))); + })); + } + + @CheckParams(minSize = 1, maxSize = 2000) + public Property tails() { + return property(arbList(arbInteger), list -> implies(list.isNotEmpty(), () -> + prop(list.tails().length() == list.length() + 1 && + List.join(list.inits()).length() == Stream.range(1, list.length() + 1).foldLeft((acc, i) -> acc + i, 0)))); + } + + @CheckParams(minSize = 1, maxSize = 2000) + public Property inits() { + return property(arbList(arbInteger), list -> implies(list.isNotEmpty(), () -> + prop(list.inits().length() == list.length() + 1 && + List.join(list.tails()).length() == Stream.range(1, list.length() + 1).foldLeft((acc, i) -> acc + i, 0)))); + } + + public Property sort() { + return property(arbList(arbInteger), list -> { + java.util.List javaList = list.sort(intOrd).toJavaList(); + java.util.List copy = new ArrayList<>(javaList); + Collections.sort(copy); + return prop(javaList.equals(copy)); + }); + } + + public Property forallExists() { + return property(arbList(arbInteger), list -> + prop(list.forall(x -> x % 2 == 0) == !list.exists(x -> x % 2 != 0))); + } + + public Property find() { + return property(arbList(arbInteger), list -> prop(list.find(x -> x % 2 == 0).forall(x -> x % 2 == 0))); + } + + @CheckParams(maxSize = 500) + public Property join() { + return property(arbList(arbList(arbInteger)), (List> lists) -> + prop(eq.eq(lists.foldLeft(List::append, nil()), List.join(lists)))); + } + + @CheckParams(maxSize = 2000) + public Property nub() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + prop(eq.eq(list1.append(list2).nub(), list1.nub().append(list2.nub()).nub()))); + } + + public Property groupBy() { + return property(arbList(arbInteger), list -> { + 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()) + .and(prop(list1.forall(i -> i % 2 == 0))) + .and(prop(list2.forall(i -> i % 2 != 0))) + .and(prop(list.map(i -> i % 2 == 0).nub().length() == map.size())); + }); + } + + public Property groupByMonoid() { + return property(arbList(arbInteger), list -> { + final TreeMap map = list.groupBy(i -> i % 2 == 0, identity(), intAdditionMonoid, booleanOrd); + final int sum1 = map.get(true).orSome(0); + final int sum2 = map.get(false).orSome(0); + return prop(list.filter(i -> i % 2 == 0).foldLeft((acc, i) -> acc + i, 0) == sum1) + .and(prop(list.filter(i -> i % 2 != 0).foldLeft((acc, i) -> acc + i, 0) == sum2)); + }); + } + + public Property isPrefixOf() { + final Gen, Integer>> gen = arbList(arbInteger).bind(list -> + Gen.choose(0, list.length()).map(i -> p(list, i))); + + return property(gen, pair -> prop(pair._1().take(pair._2()).isPrefixOf(intEqual, pair._1()))); + } + + public Property isSuffixOf() { + final Gen, Integer>> gen = arbList(arbInteger).bind(list -> + Gen.choose(0, list.length()).map(i -> p(list, i))); + + return property(gen, pair -> prop(pair._1().drop(pair._2()).isSuffixOf(intEqual, pair._1()))); + } + + public Property isPrefixOfShorter() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + implies(list1.length() > list2.length(), () -> prop(!list1.isPrefixOf(intEqual, list2)))); + } + + public Property isSuffixOfShorter() { + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + implies(list1.length() > list2.length(), () -> prop(!list1.isSuffixOf(intEqual, list2)))); + } + + public Property isPrefixOfDifferentHeads() { + return property(arbList(arbInteger), arbList(arbInteger), arbInteger, arbInteger, (list1, list2, h1, h2) -> + implies(intEqual.notEq(h1, h2), () -> prop(!list1.cons(h1).isPrefixOf(intEqual, list2.cons(h2))))); + } + + public Property isSuffixOfDifferentHeads() { + return property(arbList(arbInteger), arbList(arbInteger), arbInteger, arbInteger, (list1, list2, h1, h2) -> + implies(intEqual.notEq(h1, h2), () -> prop(!list1.snoc(h1).isSuffixOf(intEqual, list2.snoc(h2))))); + } + + public Property listOrdEqual() { + return property(arbList(arbInteger), list -> prop(Ord.listOrd(Ord.intOrd).equal().eq(list, list))); + } + + public Property listOrdReverse() { + final Ord> ord = Ord.listOrd(Ord.intOrd); + return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) -> + prop(ord.compare(list1, list2) == ord.reverse().compare(list1, list2).reverse())); + } + +} diff --git a/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java b/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java new file mode 100644 index 00000000..64ce1934 --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java @@ -0,0 +1,105 @@ +package fj.data.properties; + +import fj.Equal; +import fj.Ord; +import fj.P2; +import fj.data.List; +import fj.data.NonEmptyList; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import fj.test.Property; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collections; + +import static fj.Equal.intEqual; +import static fj.Equal.listEqual; +import static fj.Equal.nonEmptyListEqual; +import static fj.Function.identity; +import static fj.data.NonEmptyList.nel; +import static fj.data.NonEmptyList.unzip; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbNonEmptyList; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static java.lang.Math.min; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class NonEmptyListProperties { + + private static final Equal> eq = nonEmptyListEqual(intEqual); + private static final Equal> listEq = listEqual(intEqual); + + public Property consHead() { + return property(arbNonEmptyList(arbInteger), arbInteger, (list, n) -> prop(intEqual.eq(list.cons(n).head(), n))); + } + + public Property consLength() { + return property(arbNonEmptyList(arbInteger), arbInteger, (list, n) -> prop(list.cons(n).length() == list.length() + 1)); + } + + public Property positiveLength() { + return property(arbNonEmptyList(arbInteger), list -> prop(list.length() > 0)); + } + + public Property appendLength() { + return property(arbNonEmptyList(arbInteger), arbNonEmptyList(arbInteger), (list1, list2) -> + prop(list1.append(list2).length() == list1.length() + list2.length())); + } + + public Property appendSingle() { + return property(arbNonEmptyList(arbInteger), arbInteger, (list, n) -> prop(eq.eq(nel(n).append(list), list.cons(n)))); + } + + public Property tailLength() { + return property(arbNonEmptyList(arbInteger), list -> prop(list.length() == 1 + list.tail().length())); + } + + public Property mapId() { + return property(arbNonEmptyList(arbInteger), list -> prop(eq.eq(list.map(identity()), list))); + } + + public Property reverse() { + return property(arbNonEmptyList(arbInteger), list -> + prop(listEq.eq(list.reverse().toList(), list.tail().reverse().snoc(list.head())))); + } + + public Property doubleReverse() { + return property(arbNonEmptyList(arbInteger), list -> prop(eq.eq(list.reverse().reverse(), list))); + } + + public Property sort() { + return property(arbNonEmptyList(arbInteger), list -> { + java.util.List javaList = list.sort(Ord.intOrd).toList().toJavaList(); + java.util.List copy = new ArrayList<>(javaList); + Collections.sort(copy); + return prop(javaList.equals(copy)); + }); + } + + public Property intersperseLength() { + return property(arbNonEmptyList(arbInteger), arbInteger, (list, n) -> + prop(list.intersperse(n).length() == 2 * list.length() - 1)); + } + + public Property zip() { + return property(arbNonEmptyList(arbInteger), arbNonEmptyList(arbInteger), (list1, list2) -> { + final int size = min(list1.length(), list2.length()); + final NonEmptyList> zipped = list1.zip(list2); + return prop(listEq.eq(zipped.map(P2::_1).toList(), list1.toList().take(size))) + .and(prop(listEq.eq(zipped.map(P2::_2).toList(), list2.toList().take(size)))); + }); + } + + public Property zipUnzip() { + return property(arbNonEmptyList(arbInteger), arbNonEmptyList(arbInteger), (list1, list2) -> { + final P2, NonEmptyList> unzipped = unzip(list1.zip(list2)); + final int size = min(list1.length(), list2.length()); + return prop(listEq.eq(unzipped._1().toList(), list1.toList().take(size))) + .and(prop(listEq.eq(unzipped._2().toList(), list2.toList().take(size)))); + }); + + } +} 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 new file mode 100644 index 00000000..eedbcc5c --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/SeqProperties.java @@ -0,0 +1,124 @@ +package fj.data.properties; + + +import fj.P; +import fj.P2; +import fj.data.Seq; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import fj.test.Gen; +import fj.test.Property; +import org.junit.runner.RunWith; + +import static fj.Function.identity; +import static fj.test.Arbitrary.*; +import static fj.test.Property.implies; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class SeqProperties { + + 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))); + + public Property consHead() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.cons(n).head().equals(n))); + } + + public Property consLength() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.cons(n).length() == seq.length() + 1)); + } + + public Property snocLast() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.snoc(n).last().equals(n))); + } + + public Property snocLength() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.snoc(n).length() == seq.length() + 1)); + } + + public Property appendEmptyLeft() { + return property(arbSeq(arbInteger), seq -> prop(Seq.empty().append(seq).equals(seq))); + } + + public Property appendEmptyRight() { + return property(arbSeq(arbInteger), seq -> prop(seq.append(Seq.empty()).equals(seq))); + } + + public Property appendLength() { + return property(arbSeq(arbInteger), arbSeq(arbInteger), (seq1, seq2) -> + prop(seq1.append(seq2).length() == seq1.length() + seq2.length())); + } + + public Property consNotEmpty() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(!seq.cons(n).isEmpty())); + } + + public Property snocNotEmpty() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(!seq.snoc(n).isEmpty())); + } + + public Property appendSingleLeft() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(Seq.single(n).append(seq).equals(seq.cons(n)))); + } + + public Property appendSingleRight() { + return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.append(Seq.single(n)).equals(seq.snoc(n)))); + } + + public Property splitLength() { + return property(arbSeq(arbInteger), arbInteger, (seq, i) -> prop(seq.length() == seq.split(i)._1().length() + seq.split(i)._2().length())); + } + + public Property tailLength() { + return property(arbSeq(arbInteger), seq -> + implies(!seq.isEmpty(), () -> prop(seq.length() == 1 + seq.tail().length()))); + } + + public Property initLength() { + return property(arbSeq(arbInteger), seq -> + implies(!seq.isEmpty(), () -> prop(seq.length() == seq.init().length() + 1))); + } + + public Property mapId() { + return property(arbSeq(arbInteger), seq -> prop(seq.map(identity()).equals(seq))); + } + + @CheckParams(minSize = 1) + public Property updateAndIndex() { + return property(arbSeqWithIndex, arbInteger, (pair, n) -> { + final Seq seq = pair._1(); + final int index = pair._2(); + return prop(seq.update(index, n).index(index).equals(n)); + }); + } + + @CheckParams(minSize = 1) + public Property delete() { + return property(arbSeqWithIndex, arbInteger, (pair, n) -> { + final Seq seq = pair._1(); + final int index = pair._2(); + return prop(seq.delete(index).length() == seq.length() - 1); + }); + } + + public Property foldLeft() { + return property(arbSeq(Gen.value(1)), seq -> + prop(seq.foldLeft((acc, i) -> acc + i, 0) == seq.length())); + } + + public Property foldRight() { + return property(arbSeq(Gen.value(1)), seq -> + prop(seq.foldRight((i, acc) -> acc + i, 0) == seq.length())); + } + + public Property length() { + return property(arbList(arbInteger), list -> + prop(Seq.iterableSeq(list).length() == list.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 new file mode 100644 index 00000000..2657b937 --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/SetProperties.java @@ -0,0 +1,48 @@ +package fj.data.properties; + +import fj.Equal; +import fj.Ord; +import fj.data.List; +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; +import org.junit.runner.RunWith; + +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class SetProperties { + + public final static int maxSize = 20; + public final static Gen> as = Arbitrary.arbSet(Ord.intOrd, Arbitrary.arbInteger, maxSize); + public final static Equal> eq = Equal.listEqual(Equal.intEqual); + + Property setToListIsSorted() { + return property(as, s -> prop(s.toList().equals(s.toList().sort(Ord.intOrd)))); + } + + Property stream() { + return property(as, s -> { + List l1 = s.toList(); + List l2 = s.toStream().toList(); + return prop(eq.eq(l1, l2)); + }); + } + + Property listReverse() { + return property(as, s -> { + return prop(eq.eq(s.toList().reverse(), s.toListReverse())); + }); + } + + Property streamReverse() { + return property(as, s -> prop(eq.eq(s.toStream().toList().reverse(), s.toStreamReverse().toList()))); + } + +} diff --git a/props-core/src/test/java/fj/data/properties/StreamProperties.java b/props-core/src/test/java/fj/data/properties/StreamProperties.java new file mode 100644 index 00000000..07bc7897 --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/StreamProperties.java @@ -0,0 +1,204 @@ +package fj.data.properties; + +import fj.*; +import fj.data.Either; +import fj.data.Stream; +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.p1Equal; +import static fj.Equal.streamEqual; +import static fj.Function.compose; +import static fj.Function.identity; +import static fj.Ord.intOrd; +import static fj.data.Stream.nil; +import static fj.data.Stream.single; +import static fj.test.Arbitrary.*; +import static fj.test.Property.implies; +import static fj.test.Property.prop; +import static fj.test.Property.property; +import static java.lang.Math.abs; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class StreamProperties { + + private static final Equal> eq = streamEqual(intEqual); + + public Property isEmpty() { + return property(arbStream(arbInteger), stream -> prop(stream.isEmpty() != stream.isNotEmpty())); + } + + public Property isNotEmpty() { + return property(arbStream(arbInteger), stream -> + prop(stream.length() > 0 == stream.isNotEmpty())); + } + + public Property orHead() { + return property(arbStream(arbInteger), arbInteger, (stream, n) -> + implies(stream.isNotEmpty(), () -> prop(intEqual.eq(stream.orHead(() -> n), stream.head())))); + } + + public Property orTail() { + final Equal>> eq = p1Equal(streamEqual(intEqual)); + return property(arbStream(arbInteger), arbP1(arbStream(arbInteger)), (stream, stream2) -> + implies(stream.isNotEmpty(), () -> prop(eq.eq(stream.orTail(stream2), stream.tail())))); + } + + public Property bindStackOverflow() { + return property(arbInteger, n -> { + final Stream stream = Stream.range(1, abs(n)); + final Stream bound = stream.bind(Stream::single); + return prop(stream.zip(bound).forall(p2 -> intEqual.eq(p2._1(), p2._2()))); + }); + } + + public Property toOption() { + return property(arbStream(arbInteger), stream -> + prop(stream.toOption().isNone() || intEqual.eq(stream.toOption().some(), stream.head()))); + } + + public Property toEither() { + return property(arbStream(arbInteger), arbP1(arbInteger), (stream, n) -> { + final Either e = stream.toEither(n); + return prop(e.isLeft() && intEqual.eq(e.left().value(), n._1()) || + intEqual.eq(e.right().value(), stream.head())); + }); + } + + public Property consHead() { + return property(arbStream(arbInteger), arbInteger, (stream, n) -> + prop(intEqual.eq(stream.cons(n).head(), n))); + } + + public Property consLength() { + return property(arbStream(arbInteger), arbInteger, (stream, n) -> + prop(stream.cons(n).length() == stream.length() + 1)); + } + + public Property mapId() { + return property(arbStream(arbInteger), stream -> prop(eq.eq(stream.map(identity()), stream))); + } + + public Property mapCompose() { + final F f = x -> x + 3; + final F g = x -> x * 4; + return property(arbStream(arbInteger), stream -> + prop(eq.eq(stream.map(compose(f, g)), stream.map(g).map(f)))); + } + + public Property foreachDoEffect() { + return property(arbStream(arbInteger), stream -> { + int[] acc = {0}; + stream.foreachDoEffect(x -> acc[0] += x); + + int acc2 = 0; + for (int x : stream) { acc2 += x; } + + return prop(intEqual.eq(acc[0], acc2)); + }); + } + + public Property filter() { + final F predicate = (x -> x % 2 == 0); + return property(arbStream(arbInteger), stream -> prop(stream.filter(predicate).forall(predicate))); + } + + public Property filterLength() { + final F predicate = (x -> x % 2 == 0); + return property(arbStream(arbInteger), stream -> prop(stream.filter(predicate).length() <= stream.length())); + } + + public Property bindLeftIdentity() { + final F> f = (i -> single(-i)); + return property(arbStream(arbInteger), arbInteger, (stream, i) -> + prop(eq.eq(single(i).bind(f), f.f(i)))); + } + + public Property bindRightIdentity() { + return property(arbStream(arbInteger), stream -> prop(eq.eq(stream.bind(Stream::single), stream))); + } + + public Property bindAssociativity() { + final F> f = x -> single(x + 3); + final F> g = x -> single(x * 4); + return property(arbStream(arbInteger), stream -> + prop(eq.eq(stream.bind(f).bind(g), stream.bind(i -> f.f(i).bind(g))))); + } + + @CheckParams(maxSize = 100) + public Property sequence() { + return property(arbStream(arbInteger), arbStream(arbInteger), (stream1, stream2) -> + prop(eq.eq(stream1.sequence(stream2), stream1.bind(__ -> stream2)))); + } + + public Property append() { + return property(arbStream(arbInteger), arbInteger, (stream, i) -> + prop(eq.eq(single(i).append(stream), stream.cons(i)))); + } + + public Property foldRight() { + return property(arbStream(arbInteger), stream -> + prop(eq.eq(stream.foldRight((i, s) -> single(i).append(s), nil()), stream))); + } + + public Property foldLeft() { + return property(arbStream(arbInteger), stream -> + prop(eq.eq(stream.foldLeft((s, i) -> single(i).append(s), nil()), + stream.reverse().foldRight((i, s) -> single(i).append(s), nil())))); + } + + public Property tailLength() { + return property(arbStream(arbInteger), stream -> + implies(stream.isNotEmpty(), () -> prop(stream.tail()._1().length() == stream.length() - 1))); + } + + public Property reverseIdentity() { + return property(arbStream(arbInteger), stream -> + prop(eq.eq(stream.reverse().reverse(), stream))); + } + + public Property reverse() { + return property(arbStream(arbInteger), arbStream(arbInteger), (stream1, stream2) -> + prop(eq.eq(stream1.append(stream2).reverse(), stream2.reverse().append(stream1.reverse())))); + } + + @CheckParams(minSize = 2, maxSize = 10000) + public Property indexTail() { + 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(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))); + }); + } + + public Property forallExists() { + return property(arbStream(arbInteger), stream -> + prop(stream.forall(x -> x % 2 == 0) == !stream.exists(x -> x % 2 != 0))); + } + + public Property find() { + return property(arbStream(arbInteger), stream -> prop(stream.find(x -> x % 2 == 0).forall(x -> x % 2 == 0))); + } + + @CheckParams(maxSize = 500) + public Property join() { + return property(arbStream(arbStream(arbInteger)), (Stream> stream) -> + prop(eq.eq(stream.foldRight((Stream i, P1> s) -> i.append(s._1()), nil()), + Stream.join(stream)))); + } + + @CheckParams(maxSize = 1000) + public Property sort() { + return property(arbStream(arbInteger), (Stream stream) -> + prop(eq.eq(stream.sort(intOrd), stream.toList().sort(intOrd).toStream()))); + } +} diff --git a/props-core/src/test/java/fj/data/properties/TreeMapProperties.java b/props-core/src/test/java/fj/data/properties/TreeMapProperties.java new file mode 100644 index 00000000..50b323ff --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/TreeMapProperties.java @@ -0,0 +1,86 @@ +package fj.data.properties; + +import fj.Equal; +import fj.Ord; +import fj.P2; +import fj.data.List; +import fj.data.Stream; +import fj.data.TreeMap; +import fj.test.Gen; +import fj.test.Property; +import fj.test.reflect.CheckParams; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicInteger; + +import static fj.Equal.intEqual; +import static fj.Equal.p2Equal; +import static fj.Equal.stringEqual; +import static fj.Ord.intOrd; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbString; +import static fj.test.Arbitrary.arbTreeMap; +import static fj.test.Property.*; + +@RunWith(PropertyTestRunner.class) +@CheckParams(maxSize = 10000) +public class TreeMapProperties { + + private static final int smallMax = 5; + private static final int midMax = 20; + 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)); + + + public Property empty() { + return property(smallArbTreeMapIS, tm -> impliesBoolean(tm.isEmpty(), tm.size() == 0)); + } + + public Property set() { + return property(arbTreeMapIS, arbInteger, arbString, (tm, k, v) -> + prop(stringEqual.eq(tm.set(k, v).get(k).some(), v)) + ); + } + + public Property updateId() { + return property(arbTreeMapIS, arbInteger, arbString, (tm, k, v) -> + prop(stringEqual.eq(tm.set(k, v).update(k, x -> x)._2().get(k).some(), v)) + ); + } + + public Property update() { + return property(arbTreeMapIS, arbInteger, arbString, arbString, (tm, k, v, v2) -> + prop(stringEqual.eq(tm.set(k, v).update(k, x -> x + v2)._2().get(k).some(), v + v2)) + ); + } + + public Property minKey() { + return property(arbTreeMapIS, tm -> + impliesBoolean(!tm.isEmpty(), () -> Equal.intEqual.eq(tm.minKey().some(), tm.toStream().head()._1())) + ); + } + + public Property maxKey() { + return property(arbTreeMapIS, tm -> + impliesBoolean(!tm.isEmpty(), + () -> Equal.intEqual.eq(tm.maxKey().some(), tm.toStream().reverse().head()._1()) + ) + ); + } + + Property listSize() { + return property(arbTreeMapIS, tm -> prop(tm.size() == tm.toList().length())); + } + + Property listSorted() { + return property(arbTreeMapIS, tm -> prop(listEq.eq(tm.toList(), tm.toList().sort(p2Ord)))); + } + + Property listStreamEq() { + return property(arbTreeMapIS, tm -> prop(listEq.eq(tm.toList(), tm.toStream().toList()))); + } + +} diff --git a/props-core/src/test/java/fj/data/properties/ValidationProperties.java b/props-core/src/test/java/fj/data/properties/ValidationProperties.java new file mode 100644 index 00000000..a47383ab --- /dev/null +++ b/props-core/src/test/java/fj/data/properties/ValidationProperties.java @@ -0,0 +1,60 @@ +package fj.data.properties; + +import fj.P2; +import fj.Semigroup; +import fj.data.List; +import fj.data.Validation; +import fj.test.Gen; +import fj.test.Property; +import fj.test.runner.PropertyTestRunner; +import org.junit.runner.RunWith; + +import static fj.test.Arbitrary.*; +import static fj.test.Property.implies; +import static fj.test.Property.prop; + +@RunWith(PropertyTestRunner.class) +public class ValidationProperties { + + public Property partition() { + 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(); + boolean b2 = p._1().map(s -> Validation.fail(s)).equals(list.filter(v -> v.isFail())); + boolean b3 = p._2().map(s -> Validation.success(s)).equals(list.filter(v -> v.isSuccess())); + return prop(b1 && b2 && b3); + }); + } + + public Property sequenceNonCumulative() { + Gen>> al = arbList(arbValidation(arbUSASCIIString, arbInteger)); + return Property.property(al, list -> { + Validation, List> v = Validation.sequenceNonCumulative(list); + Property p1 = implies( + list.exists(v1 -> v1.isFail()), + () -> prop(v.fail().equals(list.filter(v2 -> v2.isFail()).map(v2 -> v2.fail()))) + ); + Property p2 = implies( + list.forall(v1 -> v1.isSuccess()), + () -> prop(v.success().equals(list.filter(v2 -> v2.isSuccess()).map(v2 -> v2.success()))) + ); + return p1.and(p2); + }); + } + + public Property sequence() { + return Property.property(arbList(arbValidation(arbString, arbInteger)), list -> { + Validation> v = Validation.sequence(Semigroup.stringSemigroup, list); + Property p1 = implies(list.exists((Validation v2) -> v2.isFail()), () -> prop(v.isFail())); + boolean b = list.forall((Validation v2) -> v2.isSuccess()); + Property p2 = implies(b, () -> { + List l2 = list.map((Validation v2) -> v2.success()); + boolean b2 = v.success().equals(l2); + return prop(b2); + }); + return p1.and(p2); + }); + } + +} diff --git a/java8/build.gradle b/quickcheck/build.gradle similarity index 53% rename from java8/build.gradle rename to quickcheck/build.gradle index 88e9a25f..c9851596 100644 --- a/java8/build.gradle +++ b/quickcheck/build.gradle @@ -1,16 +1,15 @@ -archivesBaseName = "${project.projectName}-${project.name}" - ext { signModule = true + uploadModule = true } +archivesBaseName = "${project.projectName}-${project.name}" + dependencies { - compile project(":core") - testCompile 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 new file mode 100644 index 00000000..d37c69a3 --- /dev/null +++ b/quickcheck/src/main/java/fj/data/test/PropertyAssert.java @@ -0,0 +1,19 @@ +package fj.data.test; + +import fj.Unit; +import fj.test.CheckResult; +import fj.test.Property; +import org.junit.Assert; + +public final class PropertyAssert { + + private PropertyAssert(){} + + public static Unit assertResult(Property p) { + CheckResult cr = p.check(); + CheckResult.summary.println(cr); + Assert.assertTrue(cr.isExhausted() || cr.isPassed() || cr.isProven()); + return Unit.unit(); + } + +} diff --git a/quickcheck/src/main/java/fj/test/Arbitrary.java b/quickcheck/src/main/java/fj/test/Arbitrary.java new file mode 100644 index 00000000..04f1e20b --- /dev/null +++ b/quickcheck/src/main/java/fj/test/Arbitrary.java @@ -0,0 +1,1327 @@ +package fj.test; + +import fj.F; +import fj.F2; +import fj.F3; +import fj.F4; +import fj.F5; +import fj.F6; +import fj.F7; +import fj.F8; +import fj.Function; +import fj.Bottom; + +import static fj.Function.compose; +import static fj.P.p; + +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.control.parallel.ParModule; +import fj.control.parallel.Strategy; +import fj.data.*; +import fj.LcgRng; +import fj.Ord; + +import static fj.data.Enumerator.charEnumerator; +import static fj.data.List.asString; +import static fj.data.List.list; + +import fj.data.List; +import fj.data.Set; + +import static fj.data.Stream.range; +import static fj.test.Gen.choose; +import static fj.test.Gen.elements; +import static fj.test.Gen.fail; +import static fj.test.Gen.frequency; +import static fj.test.Gen.listOf; +import static fj.test.Gen.oneOf; +import static fj.test.Gen.promote; +import static fj.test.Gen.sized; +import static fj.test.Gen.value; + +import static java.lang.Math.abs; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Time; +import java.sql.Timestamp; +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.PriorityQueue; +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.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; + +/** + * Common Gen helper functions. + * + * @version %build.number% + */ +public final class Arbitrary { + + /** + * An arbitrary for functions. + * + * @param c The cogen for the function domain. + * @param a The arbitrary for the function codomain. + * @return An arbitrary for functions. + */ + public static Gen> arbF(final Cogen c, final Gen a) { + return promote(x -> c.cogen(x, a)); + } + + public static Gen> arbReader(Cogen aa, Gen ab) { + return arbF(aa, ab).map(Reader::unit); + } + + /** + * An arbitrary for state. + */ + 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 Gen arbLcgRng() { + return Arbitrary.arbLong.map(LcgRng::new); + } + + /** + * An arbitrary for functions. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for functions. + */ + public static Gen> arbFInvariant(final Gen a) { + return a.map(Function.constant()); + } + + /** + * An arbitrary for function-2. + * + * @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 Gen> arbF2(final Cogen ca, final Cogen cb, + final Gen a) { + return arbF(ca, arbF(cb, a)).map(Function.uncurryF2()); + } + + /** + * An arbitrary for function-2. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-2. + */ + 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 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 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()); + } + + /** + * An arbitrary for function-3. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-3. + */ + public static Gen> arbF3Invariant(final Gen a) { + return a.map(compose(Function.uncurryF3(), compose(Function.constant(), + compose( + Function.constant(), + Function.constant())))); + } + + /** + * An arbitrary for function-4. + * + * @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 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()); + } + + /** + * An arbitrary for function-4. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-4. + */ + 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 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 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()); + } + + /** + * An arbitrary for function-5. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-5. + */ + 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 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 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()); + } + + /** + * An arbitrary for function-6. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-6. + */ + public static Gen> arbF6Invariant(final Gen a) { + return a.map(compose(Function.uncurryF6(), + compose(Function.>>>>>constant(), + compose(Function.>>>>constant(), + compose(Function.>>>constant(), + compose(Function.>>constant(), + compose(Function.>constant(), + Function.constant()))))))); + } + + /** + * An arbitrary for function-7. + * + * @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 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()); + } + + /** + * An arbitrary for function-7. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-7. + */ + public static Gen> arbF7Invariant(final Gen a) { + return a.map(compose(Function.uncurryF7(), + compose(Function.>>>>>>constant(), + compose(Function.>>>>>constant(), + compose(Function.>>>>constant(), + compose(Function.>>>constant(), + compose(Function.>>constant(), + compose(Function.>constant(), + Function.constant())))))))); + } + + /** + * An arbitrary for function-8. + * + * @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 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()); + } + + /** + * An arbitrary for function-8. + * + * @param a The arbitrary for the function codomain. + * @return An arbitrary for function-8. + */ + 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 Gen arbBoolean = elements(true, false); + + /** + * An arbitrary implementation for integer values. + */ + 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Gen arbString = + arbList(arbCharacter).map(List::asString); + + /** + * An arbitrary implementation for string values with characters in the US-ASCII range. + */ + 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 Gen arbAlphaNumString = + arbList(elements(range(charEnumerator, 'a', 'z').append( + range(charEnumerator, 'A', 'Z')).append( + range(charEnumerator, '0', '9')).toArray().array(Character[].class))).map(asString()); + + /** + * An arbitrary implementation for string buffer values. + */ + public static final Gen arbStringBuffer = + arbString.map(StringBuffer::new); + + /** + * An arbitrary implementation for string builder values. + */ + public static final Gen arbStringBuilder = + arbString.map(StringBuilder::new); + + /** + * Returns an arbitrary implementation for generators. + * + * @param aa an arbitrary implementation for the type over which the generator is defined. + * @return An arbitrary implementation for generators. + */ + public static Gen> arbGen(final Gen aa) { + return sized(i -> { + if (i == 0) + return fail(); + else + return aa.map(Gen::value).resize(i - 1); + }); + } + + /** + * Returns an arbitrary implementation for optional values. + * + * @param aa an arbitrary implementation for the type over which the optional value is defined. + * @return An arbitrary implementation for optional values. + */ + public static Gen> arbOption(final Gen aa) { + return sized(i -> i == 0 ? + value(Option.none()) : + aa.map(Option::some).resize(i - 1)); + } + + /** + * Returns an arbitrary implementation for the disjoint union. + * + * @param aa An arbitrary implementation for the type over which one side of the disjoint union is + * defined. + * @param ab An arbitrary implementation for the type over which one side of the disjoint union is + * defined. + * @return An arbitrary implementation for the disjoint union. + */ + @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)); + } + + /** + * Returns an arbitrary implementation for lists. + * + * @param aa An arbitrary implementation for the type over which the list is defined. + * @return An arbitrary implementation for lists. + */ + public static Gen> arbList(final Gen aa) { + return listOf(aa); + } + + /** + * Returns an arbitrary list of integers. + */ + public static Gen> arbListInteger() { + return listOf(arbInteger); + } + + /** + * Returns an arbitrary list of strings. + */ + public static Gen> arbListString() { + return listOf(arbString); + } + + /** + * Returns an arbitrary list of booleans. + */ + public static Gen> arbListBoolean() { + return listOf(arbBoolean); + } + + /** + * Returns an arbitrary list of doubles. + */ + public static Gen> arbListDouble() { + return listOf(arbDouble); + } + + 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 Gen> arbValidation(final Gen aa, final Gen ab) { + return arbBoolean.bind(bool -> bool ? ab.map(Validation::success) : aa.map(Validation::fail)); + } + + /** + * Returns an arbitrary implementation for streams. + * + * @param aa An arbitrary implementation for the type over which the stream is defined. + * @return An arbitrary implementation for streams. + */ + public static Gen> arbStream(final Gen aa) { + return arbList(aa).map(List::toStream); + } + + /** + * Returns an arbitrary implementation for arrays. + * + * @param aa An arbitrary implementation for the type over which the array is defined. + * @return An arbitrary implementation for arrays. + */ + public static Gen> arbArray(final Gen aa) { + return arbList(aa).map(List::toArray); + } + + /** + * Returns an arbitrary implementation for sequences. + * + * @param aa An arbitrary implementation for the type over which the sequence is defined. + * @return An arbitrary implementation for sequences. + */ + @SuppressWarnings("unchecked") + public static Gen> arbSeq(final Gen aa) { + return arbArray(aa).map(Seq::iterableSeq); + } + + public static Gen> arbSet(Ord ord, final Gen aa) { + return arbList(aa).map(list -> Set.iterableSet(ord, list)); + } + + 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)); + } + + + /** + * Returns an arbitrary implementation for throwables. + * + * @param as An arbitrary used for the throwable message. + * @return An arbitrary implementation for throwables. + */ + public static Gen arbThrowable(final Gen as) { + return as.map(Throwable::new); + } + + /** + * An arbitrary implementation for throwables. + */ + public static final Gen arbThrowable = arbThrowable(arbString); + + // BEGIN java.util + + /** + * Returns an arbitrary implementation for array lists. + * + * @param aa An arbitrary implementation for the type over which the array list is defined. + * @return An arbitrary implementation for array lists. + */ + public static Gen> arbArrayList(final Gen aa) { + return arbArray(aa).map(Array::toJavaList); + } + + /** + * An arbitrary implementation for bit sets. + */ + 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 Gen arbCalendar = arbLong.map(i -> { + final Calendar c = Calendar.getInstance(); + c.setTimeInMillis(i); + return c; + }); + + /** + * An arbitrary implementation for dates. + */ + 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 arbitrary of. + * @return An arbitrary for instances of the supplied enum type. + */ + public static > Gen arbEnumValue(final Class clazz) { + return elements(clazz.getEnumConstants()); + } + + /** + * Returns an arbitrary implementation for enum maps. + * + * @param ak An arbitrary implementation for the type over which the enum map's keys are defined. + * @param av An arbitrary implementation for the type over which the enum map's values are + * defined. + * @return An arbitrary implementation for enum maps. + */ + public static , V> Gen> arbEnumMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(EnumMap::new); + } + + /** + * Returns an arbitrary implementation for enum sets. + * + * @param aa An arbitrary implementation for the type over which the enum set is defined. + * @return An arbitrary implementation for enum sets. + */ + public static > Gen> arbEnumSet(final Gen aa) { + return arbArray(aa).map(a -> copyOf(a.asJavaList())); + } + + /** + * An arbitrary implementation for gregorian calendars. + */ + 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. + * + * @param ak An arbitrary implementation for the type over which the hash map's keys are defined. + * @param av An arbitrary implementation for the type over which the hash map's values are + * defined. + * @return An arbitrary implementation for hash maps. + */ + public static Gen> arbHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(HashMap::new); + } + + /** + * Returns an arbitrary implementation for hash sets. + * + * @param aa An arbitrary implementation for the type over which the hash set is defined. + * @return An arbitrary implementation for hash sets. + */ + public static Gen> arbHashSet(final Gen aa) { + return arbArray(aa).map(a -> new HashSet<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for hash tables. + * + * @param ak An arbitrary implementation for the type over which the hash table's keys are + * defined. + * @param av An arbitrary implementation for the type over which the hash table's values are + * defined. + * @return An arbitrary implementation for hash tables. + */ + 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; + }); + } + + /** + * Returns an arbitrary implementation for identity hash maps. + * + * @param ak An arbitrary implementation for the type over which the identity hash map's keys are + * defined. + * @param av An arbitrary implementation for the type over which the identity hash map's values + * are defined. + * @return An arbitrary implementation for identity hash maps. + */ + public static Gen> arbIdentityHashMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(IdentityHashMap::new); + } + + /** + * Returns an arbitrary implementation for linked hash maps. + * + * @param ak An arbitrary implementation for the type over which the linked hash map's keys are + * defined. + * @param av An arbitrary implementation for the type over which the linked hash map's values are + * defined. + * @return An arbitrary implementation for linked hash maps. + */ + public static Gen> arbLinkedHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(LinkedHashMap::new); + } + + /** + * Returns an arbitrary implementation for hash sets. + * + * @param aa An arbitrary implementation for the type over which the hash set is defined. + * @return An arbitrary implementation for hash sets. + */ + public static Gen> arbLinkedHashSet(final Gen aa) { + return arbArray(aa).map(a -> new LinkedHashSet<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for linked lists. + * + * @param aa An arbitrary implementation for the type over which the linked list is defined. + * @return An arbitrary implementation for linked lists. + */ + public static Gen> arbLinkedList(final Gen aa) { + return arbArray(aa).map(a -> new LinkedList<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for priority queues. + * + * @param aa An arbitrary implementation for the type over which the priority queue is defined. + * @return An arbitrary implementation for priority queues. + */ + public static Gen> arbPriorityQueue(final Gen aa) { + return arbArray(aa).map(a -> new PriorityQueue<>(a.asJavaList())); + } + + /** + * An arbitrary implementation for properties. + */ + public static final Gen arbProperties = + arbHashtable(arbString, arbString).map(ht -> { + final Properties p = new Properties(); + + for (final Map.Entry entry : ht.entrySet()) { + p.setProperty(entry.getKey(), entry.getValue()); + } + + return p; + }); + + /** + * Returns an arbitrary implementation for stacks. + * + * @param aa An arbitrary implementation for the type over which the stack is defined. + * @return An arbitrary implementation for stacks. + */ + public static Gen> arbStack(final Gen aa) { + return arbArray(aa).map(a -> { + final Stack s = new Stack<>(); + s.addAll(a.asJavaList()); + return s; + }); + } + + /** + * Returns an arbitrary implementation for java.util tree maps. + * + * @param ak An arbitrary implementation for the type over which the tree map's keys are defined. + * @param av An arbitrary implementation for the type over which the tree map's values are + * defined. + * @return An arbitrary implementation for tree maps. + */ + 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 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 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 Gen> arbTreeMap(Ord ord, Gen ak, Gen av, Gen ai) { + Gen>> gl2 = ai.bind(i -> { + if (i < 0) { + throw Bottom.error("Undefined: arbitrary natural is negative (" + i + ")"); + } + return Gen.sequenceN(Math.max(i, 0), arbP2(ak, av)); + }); + 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 Gen> arbTreeMap(Ord ord, Gen ak, Gen av, int maxSize) { + if (maxSize < 0) { + throw Bottom.error("Undefined: arbitrary natural is negative (" + maxSize + ")"); + } + return arbTreeMap(ord, ak, av, choose(0, maxSize)); + } + + /** + * Returns an arbitrary implementation for tree sets. + * + * @param aa An arbitrary implementation for the type over which the tree set is defined. + * @return An arbitrary implementation for tree sets. + */ + public static Gen> arbTreeSet(final Gen aa) { + return arbArray(aa).map(a -> new TreeSet<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for vectors. + * + * @param aa An arbitrary implementation for the type over which the vector is defined. + * @return An arbitrary implementation for vectors. + */ + @SuppressWarnings("UseOfObsoleteCollectionType") + public static Gen> arbVector(final Gen aa) { + return arbArray(aa).map(a -> new Vector<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for weak hash maps. + * + * @param ak An arbitrary implementation for the type over which the weak hash map's keys are + * defined. + * @param av An arbitrary implementation for the type over which the weak hash map's values are + * defined. + * @return An arbitrary implementation for weak hash maps. + */ + public static Gen> arbWeakHashMap(final Gen ak, final Gen av) { + return arbHashtable(ak, av).map(WeakHashMap::new); + } + + // END java.util + + // BEGIN java.util.concurrent + + /** + * Returns an arbitrary implementation for array blocking queues. + * + * @param aa An arbitrary implementation for the type over which the array blocking queue is + * defined. + * @return An arbitrary implementation for array blocking queues. + */ + 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())); + } + + /** + * Returns an arbitrary implementation for concurrent hash maps. + * + * @param ak An arbitrary implementation for the type over which the concurrent hash map's keys + * are defined. + * @param av An arbitrary implementation for the type over which the concurrent hash map's values + * are defined. + * @return An arbitrary implementation for concurrent hash maps. + */ + public static Gen> arbConcurrentHashMap(final Gen ak, + final Gen av) { + return arbHashtable(ak, av).map(ConcurrentHashMap::new); + } + + /** + * Returns an arbitrary implementation for concurrent linked queues. + * + * @param aa An arbitrary implementation for the type over which the concurrent linked queue is + * defined. + * @return An arbitrary implementation for concurrent linked queues. + */ + public static Gen> arbConcurrentLinkedQueue(final Gen aa) { + return arbArray(aa).map(a -> new ConcurrentLinkedQueue<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for copy-on-write array lists. + * + * @param aa An arbitrary implementation for the type over which the copy-on-write array list is + * defined. + * @return An arbitrary implementation for copy-on-write array lists. + */ + public static Gen> arbCopyOnWriteArrayList(final Gen aa) { + return arbArray(aa).map(a -> new CopyOnWriteArrayList<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for copy-on-write array sets. + * + * @param aa An arbitrary implementation for the type over which the copy-on-write array set is + * defined. + * @return An arbitrary implementation for copy-on-write array sets. + */ + public static Gen> arbCopyOnWriteArraySet(final Gen aa) { + return arbArray(aa).map(a -> new CopyOnWriteArraySet<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for delay queues. + * + * @param aa An arbitrary implementation for the type over which the delay queue is defined. + * @return An arbitrary implementation for delay queues. + */ + public static Gen> arbDelayQueue(final Gen aa) { + return arbArray(aa).map(a -> new DelayQueue<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for linked blocking queues. + * + * @param aa An arbitrary implementation for the type over which the linked blocking queue is + * defined. + * @return An arbitrary implementation for linked blocking queues. + */ + public static Gen> arbLinkedBlockingQueue(final Gen aa) { + return arbArray(aa).map(a -> new LinkedBlockingQueue<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for priority blocking queues. + * + * @param aa An arbitrary implementation for the type over which the priority blocking queue is + * defined. + * @return An arbitrary implementation for priority blocking queues. + */ + public static Gen> arbPriorityBlockingQueue(final Gen aa) { + return arbArray(aa).map(a -> new PriorityBlockingQueue<>(a.asJavaList())); + } + + /** + * Returns an arbitrary implementation for priority blocking queues. + * + * @param aa An arbitrary implementation for the type over which the priority blocking queue is + * defined. + * @return An arbitrary implementation for priority blocking queues. + */ + 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 + + // BEGIN java.sql + + /** + * An arbitrary implementation for SQL dates. + */ + public static final Gen arbSQLDate = arbLong.map(java.sql.Date::new); + + /** + * An arbitrary implementation for SQL times. + */ + public static final Gen Gen> arbP1(final Gen aa) { + return aa.map(P::p); + } + + /** + * Returns an arbitrary implementation for product-2 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-2 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-2 is + * defined. + * @return An arbitrary implementation for product-2 values. + */ + public static Gen> arbP2(final Gen aa, final Gen ab) { + return aa.bind(ab, a -> b -> p(a, b)); + } + + /** + * Returns an arbitrary implementation for product-3 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-3 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-3 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-3 is + * defined. + * @return An arbitrary implementation for product-3 values. + */ + 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)); + } + + /** + * Returns an arbitrary implementation for product-4 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-4 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-4 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-4 is + * defined. + * @param ad An arbitrary implementation for one of the types over which the product-4 is + * defined. + * @return An arbitrary implementation for product-4 values. + */ + 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)); + } + + /** + * Returns an arbitrary implementation for product-5 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-5 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-5 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-5 is + * defined. + * @param ad An arbitrary implementation for one of the types over which the product-5 is + * defined. + * @param ae An arbitrary implementation for one of the types over which the product-5 is + * defined. + * @return An arbitrary implementation for product-5 values. + */ + 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)); + } + + /** + * Returns an arbitrary implementation for product-6 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-6 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-6 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-6 is + * defined. + * @param ad An arbitrary implementation for one of the types over which the product-6 is + * defined. + * @param ae An arbitrary implementation for one of the types over which the product-6 is + * defined. + * @param af An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @return An arbitrary implementation for product-6 values. + */ + 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)); + } + + /** + * Returns an arbitrary implementation for product-7 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param ad An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param ae An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param af An arbitrary implementation for one of the types over which the product-7 is + * defined. + * @param ag An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @return An arbitrary implementation for product-7 values. + */ + 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)); + } + + /** + * Returns an arbitrary implementation for product-8 values. + * + * @param aa An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ab An Arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ac An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ad An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ae An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param af An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ag An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @param ah An arbitrary implementation for one of the types over which the product-8 is + * defined. + * @return An arbitrary implementation for product-8 values. + */ + 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/core/src/main/java/fj/test/Arg.java b/quickcheck/src/main/java/fj/test/Arg.java similarity index 78% rename from core/src/main/java/fj/test/Arg.java rename to quickcheck/src/main/java/fj/test/Arg.java index 337f8027..71df9e2d 100644 --- a/core/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/core/src/main/java/fj/test/Bool.java b/quickcheck/src/main/java/fj/test/Bool.java similarity index 88% rename from core/src/main/java/fj/test/Bool.java rename to quickcheck/src/main/java/fj/test/Bool.java index 3c70a00f..18239fa6 100644 --- a/core/src/main/java/fj/test/Bool.java +++ b/quickcheck/src/main/java/fj/test/Bool.java @@ -1,6 +1,7 @@ package fj.test; -import fj.P1; +import fj.F0; + import static fj.test.Property.prop; /** @@ -43,7 +44,7 @@ public boolean isNot() { * @param p The property to return if this value is true. * @return a property that produces a result only if this value is true. */ - public Property implies(final P1 p) { + public Property implies(final F0 p) { return Property.implies(b, p); } @@ -55,11 +56,7 @@ public Property implies(final P1 p) { * @return a property that produces a result only if this value is true. */ public Property implies(final Property p) { - return Property.implies(b, new P1() { - public Property _1() { - return p; - } - }); + return Property.implies(b, () -> p); } /** @@ -79,11 +76,7 @@ public Property implies(final Bool c) { * @return a property that produces a result only if this value is true. */ public Property implies(final boolean c) { - return Property.implies(b, new P1() { - public Property _1() { - return prop(c); - } - }); + return Property.implies(b, () -> prop(c)); } /** diff --git a/core/src/main/java/fj/test/CheckResult.java b/quickcheck/src/main/java/fj/test/CheckResult.java similarity index 89% rename from core/src/main/java/fj/test/CheckResult.java rename to quickcheck/src/main/java/fj/test/CheckResult.java index b5b90685..784fe4e9 100644 --- a/core/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).show(r).toString(); - if (r.isProven() || r.isPassed() || r.isExhausted()) - return s; - else if (r.isFalsified() || r.isPropException() || r.isGenException()) - throw new Error(s); - else - throw decons(r.getClass()); - } + return showS(r -> { + final String s = summary(sa).showS(r); + if (r.isProven() || r.isPassed() || r.isExhausted()) + return s; + else if (r.isFalsified() || r.isPropException() || r.isGenException()) + throw new Error(s); + else + throw decons(r.getClass()); }); } } diff --git a/quickcheck/src/main/java/fj/test/Cogen.java b/quickcheck/src/main/java/fj/test/Cogen.java 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/core/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java similarity index 60% rename from core/src/main/java/fj/test/Gen.java rename to quickcheck/src/main/java/fj/test/Gen.java index 2a7b0807..8fe1e6f5 100644 --- a/core/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))); } /** @@ -125,8 +120,7 @@ public B f(final Rand r) { * @return A generator that produces values that meet the given predicate. */ public Gen
filter(final F f) { - return gen(curry(new F2() { - public A f(final Integer i, final Rand r) { + return gen(curry((i, r) -> { A a; do { @@ -134,7 +128,6 @@ public A f(final Integer i, final Rand r) { } while(!f.f(a)); return a; - } })); } @@ -157,7 +150,7 @@ public Unit foreach(final Integer i, final Rand r, final F f) { * @param r The random generator to generate the result to apply the side-effect to. * @param f The side-effect to execute on the generated value. */ - public void foreach(final Integer i, final Rand r, final Effect1 f) { + public void foreachDoEffect(final Integer i, final Rand r, final Effect1 f) { f.f(this.f.f(i).f(r)); } @@ -168,15 +161,7 @@ public void foreach(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)); } /** @@ -291,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); } /** @@ -309,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)); } /** @@ -327,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); } /** @@ -337,15 +306,7 @@ public static Gen gen(final F> f) { * @return A generator of lists after sequencing the given generators. */ public static Gen> sequence(final List> gs) { - return gs.foldRight(new F, F>, Gen>>>() { - public F>, Gen>> f(final Gen ga) { - return new F>, Gen>>() { - public Gen> f(final Gen> gas) { - return ga.bind(gas, List.cons()); - } - }; - } - }, value(List.nil())); + return gen(i -> r -> gs.map(g -> g.gen(i, r))); } /** @@ -359,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. @@ -367,11 +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(new F2() { - public A f(final Integer i, final Rand r) { - return f.f(i).f(r).gen(i, r); - } - })); + return new Gen<>(curry((i, r) -> f.f(i).f(r).gen(i, r))); } /** @@ -381,7 +351,7 @@ public A f(final Integer i, final Rand r) { * @return A new generator. */ public static Gen sized(final F> f) { - return parameterised(flip(Function.>>constant(f))); + return parameterised(flip(Function.constant(f))); } /** @@ -391,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); } /** @@ -412,11 +374,13 @@ public A f(final Rand r) { public static Gen choose(final int from, final int to) { final int f = min(from, to); final int t = max(from, to); - return parameterised(curry(new F2>() { - public Gen f(final Integer i, final Rand r) { - return value(r.choose(f, t)); - } - })); + 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))); } /** @@ -429,15 +393,7 @@ public Gen f(final Integer i, final Rand r) { 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))); } /** @@ -446,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"); }); } @@ -464,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()); } /** @@ -478,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 { @@ -490,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)); } /** @@ -505,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))); } /** @@ -522,12 +460,9 @@ public Gen f(final A a) { * @param as The values that the returned generator may produce. * @return A generator that produces values from the given arguments. */ + @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]); } /** @@ -539,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); } /** @@ -554,15 +485,11 @@ public Gen f(final Integer i) { * @return A generator of lists whose values come from the given generator. */ public static Gen> listOf(final Gen g, final int x) { - return sized(new F>>() { - public Gen> f(final Integer size) { - return choose(x, size).bind(new F>>() { - public Gen> f(final Integer n) { - return sequenceN(n, g); - } - }); - } - }); + 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)); } /** @@ -576,7 +503,7 @@ public static Gen> listOf(final Gen g) { } /** - * Returns a generator of lists whose values come from the given generator. + * Returns a generator of non empty lists whose values come from the given generator. * * @param g The generator to produce values from for the returned generator. * @return A generator of lists whose values come from the given generator. @@ -586,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)); } /** @@ -635,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/core/src/main/java/fj/test/Property.java b/quickcheck/src/main/java/fj/test/Property.java similarity index 66% rename from core/src/main/java/fj/test/Property.java rename to quickcheck/src/main/java/fj/test/Property.java index d914d092..2a327ebc 100644 --- a/core/src/main/java/fj/test/Property.java +++ b/quickcheck/src/main/java/fj/test/Property.java @@ -3,17 +3,9 @@ import static fj.Function.curry; import static fj.Function.compose2; import static fj.P.p; -import fj.F; -import fj.F2; -import fj.F3; -import fj.F4; -import fj.F5; -import fj.F6; -import fj.F7; -import fj.F8; -import fj.P; -import fj.P1; -import fj.P2; + +import fj.*; + import static fj.P2.__2; import fj.data.List; import fj.data.Option; @@ -62,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)); } /** @@ -80,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())); } /** @@ -98,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())); } /** @@ -119,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())); } /** @@ -136,12 +96,12 @@ public Result f(final Result res2) { * @param r The random generator to use for checking. * @param minSuccessful The minimum number of successful tests before a result is reached. * @param maxDiscarded The maximum number of tests discarded because they did not satisfy - * pre-conditions (i.e. {@link #implies(boolean, P1)}). + * pre-conditions (i.e. {@link #implies(boolean, F0)}). * @param minSize The minimum size to use for checking. * @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, @@ -192,12 +152,12 @@ 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. * @param maxDiscarded The maximum number of tests discarded because they did not satisfy - * pre-conditions (i.e. {@link #implies(boolean, P1)}). + * pre-conditions (i.e. {@link #implies(boolean, F0)}). * @param minSize The minimum size to use for checking. * @param maxSize The maximum size to use for checking. * @return A result after checking this property. @@ -234,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. @@ -247,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. @@ -257,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. @@ -280,11 +240,11 @@ 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 - * pre-conditions (i.e. {@link #implies(boolean, P1)}). + * pre-conditions (i.e. {@link #implies(boolean, F0)}). * @return A result after checking this property. */ public CheckResult maxDiscarded(final int maxDiscarded) { @@ -297,7 +257,7 @@ public CheckResult maxDiscarded(final int maxDiscarded) { * * @param r The random generator. * @param maxDiscarded The maximum number of tests discarded because they did not satisfy - * pre-conditions (i.e. {@link #implies(boolean, P1)}). + * pre-conditions (i.e. {@link #implies(boolean, F0)}). * @return A result after checking this property. */ public CheckResult maxDiscarded(final Rand r, final int maxDiscarded) { @@ -305,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. @@ -328,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. @@ -358,18 +318,26 @@ public CheckResult maxSize(final Rand r, final int maxSize) { * @param p The property to return if the condition satisfies. * @return A property that produces a result only if the given condition satisfies. */ - public static Property implies(final boolean b, final P1 p) { - return b ? p._1() : new Property(new F>() { - public F f(final Integer i) { - return new F() { - public Result f(final Rand r) { - return noResult(); - } - }; - } - }); + public static Property implies(final boolean b, final F0 p) { + return b ? p.f() : new Property(i -> r -> noResult()); } + /** + * Returns a property that produces a result only if the given condition satisfies. The result + * will be taken from the given boolean b. + */ + public static Property impliesBoolean(final boolean a, final boolean b) { + return implies(a, () -> prop(b)); + } + + /** + * Returns a property that produces a result only if the given condition satisfies. The result + * will be taken from the given lazy boolean b. + */ + public static Property impliesBoolean(final boolean a, final F0 b) { + return implies(a, () -> prop(b.f())); + } + /** * Returns a property from the given function. * @@ -387,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); } /** @@ -408,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())); } /** @@ -418,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)); } /** @@ -440,64 +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(new P1>>() { - public Option> _1() { - return 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)); }); } @@ -511,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); } /** @@ -525,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)); } @@ -538,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); } /** @@ -551,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)); } @@ -568,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))); } /** @@ -592,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)); } /** @@ -606,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))); } /** @@ -628,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)); } /** @@ -644,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)); } @@ -660,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))); } /** @@ -674,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)); } @@ -688,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))); } /** @@ -706,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))); } /** @@ -739,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))); } /** @@ -772,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, @@ -793,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)); } @@ -816,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))); } /** @@ -856,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))); } /** @@ -896,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, @@ -920,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)); } @@ -946,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))); } /** @@ -993,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))); } /** @@ -1040,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, @@ -1067,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)); } @@ -1096,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, @@ -1109,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$))); } /** @@ -1150,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$))); } /** @@ -1204,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, @@ -1234,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)); } @@ -1266,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, @@ -1281,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))); } /** @@ -1327,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))); } /** @@ -1388,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, @@ -1421,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)); } @@ -1456,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, @@ -1473,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))); } /** @@ -1524,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))); } /** @@ -1592,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, @@ -1628,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)); } @@ -1648,19 +1300,14 @@ public Property f(final H h) { * @return A property that has a result of exception, if the evaluation of the given property * throws an exception; otherwise, the given property is returned. */ - public static Property exception(final P1 p) { + public static Property exception(final F0 p) { try { - return p._1(); + 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 new file mode 100644 index 00000000..e47aa8a8 --- /dev/null +++ b/quickcheck/src/main/java/fj/test/Rand.java @@ -0,0 +1,156 @@ +package fj.test; + +import fj.F; +import fj.data.Option; + +import java.util.Random; + +import static fj.data.Option.some; +import static java.lang.Math.max; +import static java.lang.Math.min; + +/** + * A random number generator. + * + * @version %build.number% + */ +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) { + + this.f = f; + this.g = g; + this.onReseed = onReseed; + } + + /** + * Randomly chooses a value between the given range (inclusive). + * + * @param seed The seed to use for random generation. + * @param from The minimum value to choose. + * @param to The maximum value to choose. + * @return A random value in the given range. + */ + public int choose(final long seed, final int from, final int to) { + return f.f(some(seed)).f(from).f(to); + } + + /** + * Randomly chooses a value between the given range (inclusive). + * + * @param from The minimum value to choose. + * @param to The maximum value to choose. + * @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); + } + + 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). + * + * @param seed The seed to use for random generation. + * @param from The minimum value to choose. + * @param to The maximum value to choose. + * @return A random value in the given range. + */ + public double choose(final long seed, final double from, final double to) { + return g.f(some(seed)).f(from).f(to); + } + + /** + * Randomly chooses a value between the given range (inclusive). + * + * @param from The minimum value to choose. + * @param to The maximum value to choose. + * @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); + } + + /** + * Gives this random generator a new seed. + * + * @param seed The seed of the new random generator. + * @return A random generator with the given seed. + */ + public Rand reseed(long seed) { + return onReseed.f(seed); + } + + /** + * 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 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( + 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))); + } + + /* + * 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; + } + + /* + * 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. + */ + 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/core/src/main/java/fj/test/Result.java b/quickcheck/src/main/java/fj/test/Result.java similarity index 91% rename from core/src/main/java/fj/test/Result.java rename to quickcheck/src/main/java/fj/test/Result.java index f70e5095..8ad743bb 100644 --- a/core/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,11 +156,7 @@ public Option toOption() { * @return The result that may be {@link #noResult() noResult()}. */ public static Result noResult(final Option r) { - return r.orSome(new P1() { - public Result _1() { - return noResult(); - } - }); + return r.orSome(Result::noResult); } /** @@ -170,7 +165,7 @@ public Result _1() { * @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()); } /** @@ -180,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()); } /** @@ -190,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()); } /** @@ -200,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/core/src/main/java/fj/test/Shrink.java b/quickcheck/src/main/java/fj/test/Shrink.java similarity index 56% rename from core/src/main/java/fj/test/Shrink.java rename to quickcheck/src/main/java/fj/test/Shrink.java index 8f43d181..483293e8 100644 --- a/core/src/main/java/fj/test/Shrink.java +++ b/quickcheck/src/main/java/fj/test/Shrink.java @@ -1,6 +1,7 @@ package fj.test; -import fj.F; +import fj.*; + import static fj.P.p; import static fj.P.p2; import static fj.P.p3; @@ -10,15 +11,6 @@ import static fj.P.p7; import static fj.P.p8; -import fj.Function; -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 static fj.Primitive.Byte_Long; import static fj.Primitive.Character_Long; import static fj.Primitive.Double_Long; @@ -31,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; @@ -45,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; @@ -64,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; @@ -114,11 +109,7 @@ public Stream shrink(final A a) { * @return A shrink from this shrink and the given symmetric transformations. */ public Shrink map(final F f, final F g) { - return new Shrink(new F>() { - public Stream f(final B b) { - return Shrink.this.f.f(g.f(b)).map(f); - } - }); + return shrink(b -> Shrink.this.f.f(g.f(b)).map(f)); } @@ -131,7 +122,7 @@ public Stream f(final B b) { * value. */ public static Shrink shrink(final F> f) { - return new Shrink(f); + return new Shrink<>(f); } /** @@ -140,45 +131,19 @@ public static Shrink shrink(final F> f) { * @return A shrink strategy that cannot be reduced further. */ public static Shrink empty() { - return new Shrink(new F>() { - public Stream f(final A a) { - return nil(); - } - }); + return shrink(a -> nil()); } /** * A shrink strategy for longs using 0 as the bottom of the shrink. */ - public static final Shrink shrinkLong = new Shrink(new F>() { - public Stream f(final Long i) { - if (i == 0L) - return nil(); - else { - final Stream is = cons(0L, new P1>() { - public Stream _1() { - return iterate(new F() { - public Long f(final Long x) { - return x / 2L; - } - }, i).takeWhile(new F() { - public Boolean f(final Long x) { - return x != 0L; - } - }).map(new F() { - public Long f(final Long x) { - return i - x; - } - }); - } - }); - - return i < 0L ? cons(-i, new P1>() { - public Stream _1() { - return is; - } - }) : is; - } + public static final Shrink shrinkLong = shrink(i -> { + if (i == 0L) + return nil(); + else { + final Stream is = cons(0L, () -> iterate(x -> x / 2L, i).takeWhile(x2 -> x2 != 0L).map(x1 -> i - x1)); + + return i < 0L ? cons(-i, () -> is) : is; } }); @@ -186,7 +151,7 @@ public Stream _1() { * 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. @@ -218,6 +183,7 @@ public Stream _1() { */ 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. @@ -226,17 +192,9 @@ public Stream _1() { * @return A shrink strategy for optional values. */ public static Shrink> shrinkOption(final Shrink sa) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final Option o) { - return o.isNone() ? - Stream.>nil() : - cons(Option.none(), new P1>>() { - public Stream> _1() { - return sa.shrink(o.some()).map(Option.some_()); - } - }); - } - }); + return shrink(o -> o.isNone() ? + Stream.nil() : + cons(Option.none(), () -> sa.shrink(o.some()).map(Option.some_()))); } /** @@ -247,13 +205,9 @@ public Stream> _1() { * @return A shrink strategy for either values. */ public static Shrink> shrinkEither(final Shrink sa, final Shrink sb) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final Either e) { - return e.isLeft() ? - sa.shrink(e.left().value()).map(Either.left_()) : - sb.shrink(e.right().value()).map(Either.right_()); - } - }); + return shrink(e -> e.isLeft() ? + sa.shrink(e.left().value()).map(Either.left_()) : + sb.shrink(e.right().value()).map(Either.right_())); } /** @@ -268,56 +222,41 @@ 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; final List as1 = as.take(n1); final F, Boolean> isNotEmpty = isNotEmpty_(); - return cons(as1, new P1>>() { - public Stream> _1() { - final List as2 = as.drop(n1); - return cons(as2, new P1>>() { - public Stream> _1() { - return removeChunks(n1, as1).filter(isNotEmpty).map(new F, List>() { - public List f(final List aas) { - return aas.append(as2); - } - }).interleave(removeChunks(n2, as2).filter(isNotEmpty).map(new F, List>() { - public List f(final List aas) { - return as1.append(aas); - } - })); - } - }); - } - }); + return cons( + as1, + P.lazy(() -> + { + final List as2 = as.drop(n1); + return cons(as2, P.lazy(() -> removeChunks(n1, as1) + .filter(isNotEmpty) + .map(aas1 -> aas1.append(as2)) + .interleave(removeChunks(n2, as2) + .filter(isNotEmpty) + .map(as1::append)))); + }) + ); } } - @SuppressWarnings({"IfMayBeConditional"}) + @SuppressWarnings("IfMayBeConditional") Stream> shrinkOne(final List as) { if (as.isEmpty()) return nil(); else - return sa.shrink(as.head()).map(new F>() { - public List f(final A a) { - return as.tail().cons(a); - } - }).append(shrinkOne(as.tail()).map(new F, List>() { - public List f(final List aas) { - return aas.cons(as.head()); - } - })); + return sa.shrink(as.head()).map(a -> as.tail().cons(a)).append(shrinkOne(as.tail()).map(aas -> aas.cons(as.head()))); } } - return new Shrink>(new F, Stream>>() { - public Stream> f(final List as) { - final Util u = new Util(); - return u.removeChunks(as.length(), as).append(u.shrinkOne(as)); - } + return shrink(as -> { + final Util u = new Util(); + return u.removeChunks(as.length(), as).append(u.shrinkOne(as)); }); } @@ -328,7 +267,7 @@ public Stream> f(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()); } /** @@ -338,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()); } /** @@ -366,15 +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(new F() { - public Throwable f(final String s) { - return new Throwable(s); - } - }, new F() { - public String f(final Throwable t) { - return t.getMessage(); - } - }); + return ss.map(Throwable::new, Throwable::getMessage); } /** @@ -391,7 +322,7 @@ public String f(final Throwable t) { * @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()); } /** @@ -404,51 +335,27 @@ public static Shrink> shrinkArrayList(final Shrink sa) { * A shrink strategy for calendars. */ public static final Shrink shrinkCalendar = - shrinkLong.map(new F() { - public Calendar f(final Long i) { - final Calendar c = Calendar.getInstance(); - c.setTimeInMillis(i); - return c; - } - }, new F() { - public Long f(final Calendar c) { - return c.getTime().getTime(); - } - }); + shrinkLong.map(i -> { + final Calendar c = Calendar.getInstance(); + c.setTimeInMillis(i); + return c; + }, c -> c.getTime().getTime()); /** * A shrink strategy for dates. */ public static final Shrink shrinkDate = - shrinkLong.map(new F() { - public Date f(final Long i) { - return new Date(i); - } - }, new F() { - public Long f(final Date d) { - return 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(new F, EnumMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public EnumMap f(final Hashtable h) { - return new EnumMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final EnumMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(EnumMap::new, Hashtable::new); } /** @@ -458,44 +365,28 @@ public Hashtable f(final EnumMap m) { * @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()); } /** * A shrink strategy for gregorian calendars. */ public static final Shrink shrinkGregorianCalendar = - shrinkLong.map(new F() { - public GregorianCalendar f(final Long i) { - final GregorianCalendar c = new GregorianCalendar(); - c.setTimeInMillis(i); - return c; - } - }, new F() { - public Long f(final GregorianCalendar c) { - return c.getTime().getTime(); - } - }); + shrinkLong.map(i -> { + final GregorianCalendar c = new GregorianCalendar(); + c.setTimeInMillis(i); + return c; + }, c -> c.getTime().getTime()); /** * 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(new F, HashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public HashMap f(final Hashtable h) { - return new HashMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final HashMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(HashMap::new, Hashtable::new); } /** @@ -505,36 +396,30 @@ public Hashtable f(final HashMap m) { * @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(new F>, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final List> kvs) { - final Hashtable h = new Hashtable(); - kvs.foreachDoEffect(kv -> h.put(kv._1(), kv._2())); - return h; - } - }, new F, List>>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public List> f(final Hashtable h) { - List> x = List.nil(); - - for (final K k : h.keySet()) { - x = x.snoc(p(k, h.get(k))); - } + return shrinkList(shrinkP2(sk, sv)).map(kvs -> { + final Hashtable h = new Hashtable<>(); + kvs.foreachDoEffect(kv -> h.put(kv._1(), kv._2())); + return h; + }, h -> { + List> x = List.nil(); - return x; + for (final Map.Entry entry : h.entrySet()) { + x = x.snoc(p(entry.getKey(), entry.getValue())); } + + return x; }); } @@ -542,42 +427,22 @@ public List> f(final Hashtable h) { * 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(new F, IdentityHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public IdentityHashMap f(final Hashtable h) { - return new IdentityHashMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final IdentityHashMap m) { - return 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(new F, LinkedHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public LinkedHashMap f(final Hashtable h) { - return new LinkedHashMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final LinkedHashMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(LinkedHashMap::new, Hashtable::new); } /** @@ -587,7 +452,7 @@ public Hashtable f(final LinkedHashMap m) { * @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()); } /** @@ -597,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()); } /** @@ -607,35 +472,29 @@ 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()); } /** * A shrink strategy for properties. */ public static final Shrink shrinkProperties = shrinkHashtable(shrinkString, shrinkString) - .map(new F, Properties>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Properties f(final Hashtable h) { - final Properties p = new Properties(); - - for (final String k : h.keySet()) { - p.setProperty(k, h.get(k)); - } + .map(h -> { + final Properties p = new Properties(); - return p; + for (final Map.Entry entry : h.entrySet()) { + p.setProperty(entry.getKey(), entry.getValue()); } - }, new F>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final Properties p) { - final Hashtable t = new Hashtable(); - for (final Object s : p.keySet()) { - t.put((String) s, p.getProperty((String) s)); - } + return p; + }, p -> { + final Hashtable t = new Hashtable<>(); - return t; + for (final Object s : p.keySet()) { + t.put((String) s, p.getProperty((String) s)); } + + return t; }); /** @@ -645,28 +504,18 @@ public Hashtable f(final Properties p) { * @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(new F, TreeMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public TreeMap f(final Hashtable h) { - return new TreeMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final TreeMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(TreeMap::new, Hashtable::new); } /** @@ -676,7 +525,7 @@ public Hashtable f(final TreeMap m) { * @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()); } /** @@ -686,28 +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(new F, WeakHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public WeakHashMap f(final Hashtable h) { - return new WeakHashMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final WeakHashMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(WeakHashMap::new, Hashtable::new); } // END java.util @@ -721,28 +560,18 @@ public Hashtable f(final WeakHashMap m) { * @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(new F, ConcurrentHashMap>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public ConcurrentHashMap f(final Hashtable h) { - return new ConcurrentHashMap(h); - } - }, new F, Hashtable>() { - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - public Hashtable f(final ConcurrentHashMap m) { - return new Hashtable(m); - } - }); + return shrinkHashtable(sk, sv).map(ConcurrentHashMap::new, Hashtable::new); } /** @@ -752,7 +581,7 @@ public Hashtable f(final ConcurrentHashMap m) { * @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()); } /** @@ -762,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()); } /** @@ -772,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()); } /** @@ -782,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()); } /** @@ -792,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()); } /** @@ -802,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()); } /** @@ -812,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 @@ -823,43 +652,19 @@ public static Shrink> shrinkSynchronousQueue(final Shrin * A shrink strategy for SQL dates. */ public static final Shrink shrinkSQLDate = - shrinkLong.map(new F() { - public java.sql.Date f(final Long i) { - return new java.sql.Date(i); - } - }, new F() { - public Long f(final java.sql.Date c) { - return c.getTime(); - } - }); + shrinkLong.map(java.sql.Date::new, Date::getTime); /** * A shrink strategy for SQL times. */ public static final Shrink Shrink> shrinkP1(final Shrink sa) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P1 p) { - return sa.shrink(p._1()).map(new F>() { - public P1 f(final A a) { - return p(a); - } - }); - } - }); + return shrink(p -> sa.shrink(p._1()).map(P::p)); } /** @@ -932,11 +720,9 @@ public P1 f(final A a) { * @return a shrinking strategy for product-2 values. */ public static Shrink> shrinkP2(final Shrink sa, final Shrink sb) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P2 p) { - final F>> p2 = p2(); - return sa.shrink(p._1()).bind(sb.shrink(p._2()), p2); - } + return shrink(p -> { + final F>> p2 = p2(); + return sa.shrink(p._1()).bind(sb.shrink(p._2()), p2); }); } @@ -949,11 +735,9 @@ public Stream> f(final P2 p) { * @return a shrinking strategy for product-3 values. */ public static Shrink> shrinkP3(final Shrink sa, final Shrink sb, final Shrink sc) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P3 p) { - final F>>> p3 = p3(); - return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), p3); - } + return shrink(p -> { + final F>>> p3 = p3(); + return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), p3); }); } @@ -968,11 +752,9 @@ public Stream> f(final P3 p) { */ public static Shrink> shrinkP4(final Shrink sa, final Shrink sb, final Shrink sc, final Shrink sd) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P4 p) { - final F>>>> p4 = p4(); - return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), p4); - } + return shrink(p -> { + final F>>>> p4 = p4(); + return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), p4); }); } @@ -989,11 +771,9 @@ public Stream> f(final P4 p) { public static Shrink> shrinkP5(final Shrink sa, final Shrink sb, final Shrink sc, final Shrink sd, final Shrink se) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P5 p) { - final F>>>>> p5 = p5(); - return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), p5); - } + return shrink(p -> { + final F>>>>> p5 = p5(); + return sa.shrink(p._1()).bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), p5); }); } @@ -1011,12 +791,10 @@ public Stream> f(final P5 p) { public static Shrink> shrinkP6(final Shrink sa, final Shrink sb, final Shrink sc, final Shrink sd, final Shrink se, final Shrink sf) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P6 p) { - final F>>>>>> p6 = p6(); - return sa.shrink(p._1()) - .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), p6); - } + return shrink(p -> { + final F>>>>>> p6 = p6(); + return sa.shrink(p._1()) + .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), p6); }); } @@ -1037,13 +815,11 @@ public Stream> f(final P5 p) { final Shrink se, final Shrink sf, final Shrink sg) { - return new Shrink>(new F, Stream>>() { - public Stream> f(final P7 p) { - final F>>>>>>> p7 = p7(); - return sa.shrink(p._1()) - .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), + return shrink(p -> { + final F>>>>>>> p7 = p7(); + return sa.shrink(p._1()) + .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), sg.shrink(p._7()), p7); - } }); } @@ -1068,14 +844,13 @@ public Stream> f(final P5 p) { final Shrink sf, final Shrink sg, final Shrink sh) { - return new Shrink>( - new F, Stream>>() { - public Stream> f(final P8 p) { - final F>>>>>>>> p8 = p8(); - return sa.shrink(p._1()) - .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), - sg.shrink(p._7()), sh.shrink(p._8()), p8); - } - }); + return shrink( + p -> { + final F>>>>>>>> p8 = p8(); + return sa.shrink(p._1()) + .bind(sb.shrink(p._2()), sc.shrink(p._3()), sd.shrink(p._4()), se.shrink(p._5()), sf.shrink(p._6()), + sg.shrink(p._7()), sh.shrink(p._8()), p8); + }); } + } diff --git a/core/src/main/java/fj/test/Variant.java b/quickcheck/src/main/java/fj/test/Variant.java similarity index 71% rename from core/src/main/java/fj/test/Variant.java rename to quickcheck/src/main/java/fj/test/Variant.java index 54b3c1ab..a0ef3276 100644 --- a/core/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; @@ -49,31 +49,15 @@ private Variant() { * @param g The generator to produce the new generator from. * @return A generator that is independent of the given generator using the given value. */ + @SuppressWarnings("unchecked") public static Gen variant(final long n, final Gen g) { final LongGen p = new LongGen(n, g); final Gen gx = variantMemo.get(p); if(gx == null) { - final Gen t = gen(new F>() { - public F f(final Integer i) { - return new F() { - public A f(final Rand r) { - return g.gen(i, r.reseed(n)); - } - }; - } - }); + final Gen t = gen(i -> r -> g.gen(i, r.reseed(n))); variantMemo.put(p, t); return t; - } else return gen(new F>() { - public F f(final Integer i) { - return new F() { - @SuppressWarnings({"unchecked"}) - public A f(final Rand r) { - return (A)gx.gen(i, r); - } - }; - } - }); + } else return gen(i -> r -> (A)gx.gen(i, r)); } /** @@ -83,11 +67,7 @@ public A f(final Rand r) { * @return A curried version of {@link #variant(long, Gen)}. */ public static F, Gen> variant(final long n) { - return new F, Gen>() { - public Gen f(final Gen g) { - return variant(n, g); - } - }; + return g -> variant(n, g); } } diff --git a/core/src/main/java/fj/test/package-info.java b/quickcheck/src/main/java/fj/test/package-info.java similarity index 100% rename from core/src/main/java/fj/test/package-info.java rename to quickcheck/src/main/java/fj/test/package-info.java diff --git a/core/src/main/java/fj/test/reflect/Category.java b/quickcheck/src/main/java/fj/test/reflect/Category.java similarity index 100% rename from core/src/main/java/fj/test/reflect/Category.java rename to quickcheck/src/main/java/fj/test/reflect/Category.java diff --git a/core/src/main/java/fj/test/reflect/Check.java b/quickcheck/src/main/java/fj/test/reflect/Check.java similarity index 64% rename from core/src/main/java/fj/test/reflect/Check.java rename to quickcheck/src/main/java/fj/test/reflect/Check.java index b2adb9c9..a1eea79b 100644 --- a/core/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 error(e.toString()); - } + 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 error(e.toString()); - } + 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/core/src/main/java/fj/test/reflect/CheckParams.java b/quickcheck/src/main/java/fj/test/reflect/CheckParams.java similarity index 91% rename from core/src/main/java/fj/test/reflect/CheckParams.java rename to quickcheck/src/main/java/fj/test/reflect/CheckParams.java index e144ba44..7951b40e 100644 --- a/core/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; @@ -29,10 +29,10 @@ /** * The maximum number of tests discarded because they did not satisfy pre-conditions - * (i.e. {@link Property#implies(boolean, P1)}). + * (i.e. {@link Property#implies(boolean, F0)}). * * @return The maximum number of tests discarded because they did not satisfy pre-conditions - * (i.e. {@link Property#implies(boolean, P1)}). + * (i.e. {@link Property#implies(boolean, F0)}). */ int maxDiscarded() default 500; diff --git a/core/src/main/java/fj/test/reflect/Main.java b/quickcheck/src/main/java/fj/test/reflect/Main.java similarity index 79% rename from core/src/main/java/fj/test/reflect/Main.java rename to quickcheck/src/main/java/fj/test/reflect/Main.java index a7e8be3c..b07997c2 100644 --- a/core/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/core/src/main/java/fj/test/reflect/Name.java b/quickcheck/src/main/java/fj/test/reflect/Name.java similarity index 100% rename from core/src/main/java/fj/test/reflect/Name.java rename to quickcheck/src/main/java/fj/test/reflect/Name.java diff --git a/core/src/main/java/fj/test/reflect/NoCheck.java b/quickcheck/src/main/java/fj/test/reflect/NoCheck.java similarity index 100% rename from core/src/main/java/fj/test/reflect/NoCheck.java rename to quickcheck/src/main/java/fj/test/reflect/NoCheck.java diff --git a/core/src/main/java/fj/test/reflect/package-info.java b/quickcheck/src/main/java/fj/test/reflect/package-info.java similarity index 100% rename from core/src/main/java/fj/test/reflect/package-info.java rename to quickcheck/src/main/java/fj/test/reflect/package-info.java diff --git a/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java new file mode 100644 index 00000000..8b90d57c --- /dev/null +++ b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java @@ -0,0 +1,72 @@ +package fj.test.runner; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; + +import fj.P; +import fj.P3; +import fj.data.List; +import fj.data.Option; +import fj.test.CheckResult; +import fj.test.Property; +import fj.test.reflect.Check; +import fj.test.reflect.CheckParams; + +public class PropertyTestRunner extends Runner implements Filterable { + private final Class clas; + private final List, Description>> allTests; + private volatile List, Description>> filteredTests; + + public PropertyTestRunner(Class clas) { + this.clas = clas; + this.allTests = Check.properties(clas).map(p -> P.p(p._1(), p._3(), Description.createTestDescription(clas, p._2()))); + this.filteredTests = allTests; + } + + @Override + public final Description getDescription() { + Description suite = Description.createSuiteDescription(clas); + filteredTests.foreachDoEffect(p -> suite.addChild(p._3())); + return suite; + } + + @Override + public final void run(RunNotifier notifier) { + filteredTests.foreachDoEffect(p -> { + Description desc = p._3(); + notifier.fireTestStarted(desc); + CheckResult result = checkProperty(p._1(), p._2()); + + try { + String s = CheckResult.summaryEx.showS(result); + System.out.println(getLabel(desc) + ": " + s); + } catch (Throwable t) { + notifier.fireTestFailure(new Failure(desc, t)); + } + + notifier.fireTestFinished(desc); + }); + } + + private static String getLabel(Description d) { + return d.getDisplayName(); + } + + private static CheckResult checkProperty(Property prop, Option params) { + return params.option( + prop::check, + ps -> prop.check(ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize()) + ); + } + + @Override + 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/core/src/test/java/fj/data/test/TestCheck.java b/quickcheck/src/test/java/fj/data/test/TestCheck.java similarity index 54% rename from core/src/test/java/fj/data/test/TestCheck.java rename to quickcheck/src/test/java/fj/data/test/TestCheck.java index 8292ff61..3a4e6a80 100644 --- a/core/src/test/java/fj/data/test/TestCheck.java +++ b/quickcheck/src/test/java/fj/data/test/TestCheck.java @@ -1,34 +1,23 @@ 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.*; public class TestCheck { - @Test(timeout=1000 /*ms*/) + @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 new file mode 100644 index 00000000..0d547149 --- /dev/null +++ b/quickcheck/src/test/java/fj/data/test/TestNull.java @@ -0,0 +1,19 @@ +package fj.data.test; + +import fj.test.CheckResult; +import fj.test.Gen; +import fj.test.Property; +import org.junit.Test; + +import static fj.test.Property.prop; +import static fj.test.Property.property; + +public class TestNull { + + @Test + public void testShowNullParameters() { + 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 new file mode 100644 index 00000000..ea2cbce8 --- /dev/null +++ b/quickcheck/src/test/java/fj/test/TestRand.java @@ -0,0 +1,38 @@ +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; + +public class TestRand { + + @Test + public void testRandLowHighInclusive() { + int min = 5; + 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.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 0476f8a9..f1a6bd56 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ rootProject.name = "functionaljava" -include 'core', 'demo', "tests", "consume", "java8" +include "core", "demo", "consume", "quickcheck", "props-core", "props-core-scalacheck", "java-core", "performance" diff --git a/tests/build.gradle b/tests/build.gradle deleted file mode 100644 index d9a4e0bd..00000000 --- a/tests/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ - -apply plugin: 'scala' - -ext { - scalaVersion = "2.10.3" - scalacheckScalaVersion = "2.10" - scalacheckVersion = "1.10.1" - scalatestScalaVersion= "2.10" - scalatestVersion = "1.9.2" -} - -tasks.withType(ScalaCompile) { - scalaCompileOptions.useAnt = false -} - -dependencies { - compile project(":core") - compile "org.scala-lang:scala-library:$scalaVersion" - - testCompile "org.scala-lang:scala-library:$scalaVersion" - testCompile "org.scalacheck:scalacheck_$scalacheckScalaVersion:$scalacheckVersion" - testCompile dependencyJunit -} - -sourceCompatibility = "1.7" - diff --git a/tests/src/test/scala/fj/ArbitraryP.scala b/tests/src/test/scala/fj/ArbitraryP.scala deleted file mode 100644 index 30588f96..00000000 --- a/tests/src/test/scala/fj/ArbitraryP.scala +++ /dev/null @@ -1,11 +0,0 @@ -package fj - -import org.scalacheck.Arbitrary -import org.scalacheck.Arbitrary.arbitrary - -object ArbitraryP { - implicit def arbitraryP1[A](implicit a: Arbitrary[A]): Arbitrary[P1[A]] = - Arbitrary(arbitrary[A].map(a => new P1[A]{ - def _1 = a - })) -} \ No newline at end of file diff --git a/tests/src/test/scala/fj/Tests.scala b/tests/src/test/scala/fj/Tests.scala deleted file mode 100644 index 5c54cd2b..00000000 --- a/tests/src/test/scala/fj/Tests.scala +++ /dev/null @@ -1,49 +0,0 @@ -package fj - -object Tests { - def tests = List ( - fj.data.CheckArray.properties, - fj.data.CheckIO.properties, - fj.data.CheckIteratee.properties, - fj.data.CheckList.properties, - fj.data.CheckStream.properties, - fj.data.CheckOption.properties, - fj.data.CheckTree.properties, - fj.data.CheckHashMap.properties, - fj.data.CheckHashSet.properties, - fj.data.CheckSet.properties, - fj.data.CheckTreeMap.properties, - fj.control.parallel.CheckStrategy.properties, - fj.control.parallel.CheckParModule.properties - ).flatten - - def main(args: Array[String]) { - run(tests) -// System.exit(0) - } - - import org.scalacheck.Prop - import org.scalacheck.ConsoleReporter._ - import org.scalacheck.Test - import org.scalacheck.Test.check - - def run(tests: List[(String, Prop)]) = - tests foreach { case (name, p) => { - val c = check(new Test.Parameters.Default { }, 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.GenException(e) => { - e.printStackTrace - sys.error(name + ": " + f) - } - case f @ Test.PropException(_, e, _) => { - e.printStackTrace - sys.error(name + ": " + f) - } - } - } - } -} diff --git a/tests/src/test/scala/fj/data/ArbitraryTreeMap.scala b/tests/src/test/scala/fj/data/ArbitraryTreeMap.scala deleted file mode 100644 index 5eed461c..00000000 --- a/tests/src/test/scala/fj/data/ArbitraryTreeMap.scala +++ /dev/null @@ -1,16 +0,0 @@ -package fj -package data - -import org.scalacheck.{Arbitrary, Gen} -import TreeMap.empty; -import org.scalacheck.Arbitrary.arbitrary - -object ArbitraryTreeMap { - implicit def arbitraryTreeMap[K, V](implicit ak: Arbitrary[K], - av: Arbitrary[V], - o: Ord[K]): Arbitrary[TreeMap[K, V]] = - Arbitrary(treeOf(arbitrary[(K, V)], o)) - - def treeOf[K, V](g : => Gen[(K, V)], o : Ord[K]) : Gen[TreeMap[K, V]] = - Gen.listOf(g).map(_.foldLeft(empty(o):TreeMap[K, V])((m:TreeMap[K, V], p:(K, V)) => m.set(p._1, p._2))) -} diff --git a/tests/src/test/scala/fj/data/CheckTreeMap.scala b/tests/src/test/scala/fj/data/CheckTreeMap.scala deleted file mode 100644 index ed2a93b3..00000000 --- a/tests/src/test/scala/fj/data/CheckTreeMap.scala +++ /dev/null @@ -1,21 +0,0 @@ -package fj -package data - -import Ord.{intOrd, stringOrd} -import org.scalacheck.Prop._ -import ArbitraryTreeMap._ -import org.scalacheck.Properties - -object CheckTreeMap extends Properties("TreeMap") { - def idInt(n: Int) = n:java.lang.Integer - implicit def oi : Ord[Int] = intOrd.comap(idInt _) - implicit def os : Ord[String] = stringOrd - - property("set") = forAll((m: TreeMap[Int, String], k: Int, v: String) => m.set(k, v).get(k).some == v) - - property("updateId") = forAll((m: TreeMap[Int, String], k: Int, v: String) => - m.set(k, v).update(k, (x: String) => x)._2.get(k).some == v) - - property("update") = forAll((m: TreeMap[Int, String], k: Int, v: String, c: Char) => - m.set(k, v).update(k, (x: String) => c + x)._2.get(k).some.equals(c + v)) -} diff --git a/tests/src/test/scala/fj/package.scala b/tests/src/test/scala/fj/package.scala deleted file mode 100644 index 1fac989f..00000000 --- a/tests/src/test/scala/fj/package.scala +++ /dev/null @@ -1,12 +0,0 @@ - -package object fj { - implicit def Function1F[A, B](g: A => B): F[A, B] = new F[A, B] { - def f(a: A) = g(a) - } - - implicit def Function2F[A, B, C](g: (A, B) => C): F[A, F[B, C]] = new F[A, F[B, C]] { - def f(a: A) = new F[B, C] { - def f(b: B) = g(a, b) - } - } -} \ No newline at end of file