diff --git a/.changelogged.yaml b/.changelogged.yaml
new file mode 100644
index 00000000..46180488
--- /dev/null
+++ b/.changelogged.yaml
@@ -0,0 +1,14 @@
+changelogs:
+ - changelog: ChangeLog.md
+
+ ignore_files:
+ - "*ChangeLog*.md"
+ - .changelogged.yaml
+
+ ignore_commits: []
+
+branch: upstream/master
+
+entry_format: " - %message% (see [%link%](%id%));"
+
+editor_command: "nano -EiT 2"
diff --git a/.travis.yml b/.travis.yml
index 4a7c9290..2fb30bf7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,33 +1,31 @@
-language: java
+# Use faster, Docker-based container (instead of OpenVZ)
+sudo: false
-sudo: required
+language: java
jdk:
- - oraclejdk8
-
-addons:
- apt:
- packages:
- - oracle-java8-installer
-
-before_script:
- - sudo service postgresql stop || true
- - sudo service mysql stop || true
- - sudo service memcached stop || true
- - sudo service bootlogd stop || true
- - sudo service elasticsearch stop || true
- - sudo service mongodb stop || true
- - sudo service neo4j stop || true
- - sudo service cassandra stop || true
- - sudo service riak stop || true
- - sudo service rsync stop || true
- - sudo service x11-common stop || true
+ - openjdk8
+
+matrix:
+ fast_finish: true
+
+ include:
+
+ allow_failures:
script:
- - jdk_switcher use openjdk6 && export JAVA6_HOME=$JAVA_HOME
- - jdk_switcher use oraclejdk7 && export JAVA7_HOME=$JAVA_HOME
- - jdk_switcher use oraclejdk8 && export JAVA8_HOME=$JAVA_HOME
- - ./gradlew build coverage -s -i
+ - ./gradlew build --no-daemon
+
+env:
+ global:
+ - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU=
+ - secure: QAxhjqLRa+WHKIzgIJPZ/rM5a5uzqG7E5rsC0YvB25cO712oYXmzsYPia/oSp0chXlYLYMfk2UnLeQCSx2e6ogXRRRa977Q+B33Nt0Hd9SGLtduv6DBrbA2ehLU12Ib4DWe5VhF5eueAunycYcllTvqA5h+pzTtEVbd68ZHncM4=
+
+before_cache:
+ - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
+ - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
-after_success:
- - bash <(curl -s https://codecov.io/bash)
+cache:
+ directories:
+ - $HOME/.gradle/caches/
+ - $HOME/.gradle/wrapper/
diff --git a/README.adoc b/README.adoc
index 418605de..ecf403e2 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,6 +1,6 @@
= Functional Java
-image:https://travis-ci.org/functionaljava/functionaljava.svg?branch=master["Build Status", link="https://travis-ci.org/functionaljava/functionaljava"]
+image:https://travis-ci.org/functionaljava/functionaljava.svg?branch=master["Build Status", link="https://app.travis-ci.com/github/functionaljava/functionaljava"]
image:https://codecov.io/gh/functionaljava/functionaljava/branch/master/graph/badge.svg["Code Coverage", link="https://codecov.io/gh/functionaljava/functionaljava"]
image:https://badges.gitter.im/functionaljava/functionaljava.svg[link="https://gitter.im/functionaljava/functionaljava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
@@ -8,7 +8,7 @@ image::http://www.functionaljava.org/img/logo-600x144.png[]
Functional Java is an open source library facilitating functional programming in Java. The library implements numerous basic and advanced programming abstractions that assist composition oriented development. Functional Java also serves as a platform for learning functional programming concepts by introducing these concepts using a familiar language.
-The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module. Functional Java is compiled with Java 8 targeting Java 7 bytecode. The use of lambdas within the project are backported with the Retro Lambda library, supporting Java versions 6 to 8.
+The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module.
Functional Java provides abstractions for the following types:
@@ -22,8 +22,9 @@ Important URLs for the project are:
* Website, http://www.functionaljava.org
* Website repository, http://github.com/functionaljava/functionaljava.github.io
-* Travis continuous integration build, https://travis-ci.org/functionaljava/functionaljava
-* Sonatype Repository, https://oss.sonatype.org/content/groups/public/org/functionaljava/
+* Travis continuous integration build, https://app.travis-ci.com/github/functionaljava/functionaljava
+* Sonatype repository, https://oss.sonatype.org/content/groups/public/org/functionaljava/
+* Maven Central repository, https://mvnrepository.com/artifact/org.functionaljava/functionaljava
== Downloading
@@ -32,15 +33,14 @@ The recommended way to download and use the project is through your build tool.
The Functional Java artifact is published to Maven Central using the group `org.functionaljava` with three published artifacts:
* the core library (`functionaljava`)
-* Java 8 specific support (`functionaljava-java8`)
* property based testing (`functionaljava-quickcheck`)
+* a small amount of Java 8 support (`functionaljava-java-core`)
-The latest stable version is `4.6`. 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.6"
-compile "org.functionaljava:functionaljava-java8:4.6"
-compile "org.functionaljava:functionaljava-quickcheck:4.6"
-compile "org.functionaljava:functionaljava-java-core:4.6"
+compile "org.functionaljava:functionaljava:5.0"
+compile "org.functionaljava:functionaljava-quickcheck:5.0"
+compile "org.functionaljava:functionaljava-java-core:5.0"
----
and in Maven:
@@ -48,34 +48,27 @@ and in Maven:
org.functionaljava
functionaljava
- 4.6
-
-
- org.functionaljava
- functionaljava-java8
- 4.6
+ 5.0
org.functionaljava
functionaljava-quickcheck
- 4.6
+ 5.0
org.functionaljava
functionaljava-java-core
- 4.6
+ 5.0
----
== Building
-FunctionalJava uses the Retro Lambda project to backport Java 8 lambdas to Java 6 bytecode. This requires access to both JDK 6 and 8. The build system requires the environment variables `JAVA6_HOME` and `JAVA8_HOME` to refer to the appropriate directories.
-
-Building is done using Gradle 2.13. In the root directory run:
+Building is done using Java 8 and Gradle 7.4. In the root directory run:
----
./gradlew
----
-This requires access to Java and will download the Gradle build tool and necessary dependencies and build FunctionalJava.
+This requires access to Java 8 and will download the Gradle build tool and necessary dependencies and build FunctionalJava.
== Features
@@ -122,3 +115,7 @@ A more complete description of the features mentioned above are:
== License
link:etc/LICENCE[The Functional Java license] uses the BSD 3 license (3-clause license) available at https://en.wikipedia.org/wiki/BSD_licenses[].
+
+== Release Notes
+
+For release notes for each version, see the directory link:etc/release-notes.
diff --git a/build.gradle b/build.gradle
index 1ccd524a..c89c9536 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,29 +1,26 @@
defaultTasks 'build'
-ext {
-}
+apply plugin: "com.github.ben-manes.versions"
buildscript {
- ext {
- uptodateVersion = "1.6.0"
- retrolambdaPluginVersion = "3.2.5"
- retrolambdaVersion = "2.3.0"
- }
-
repositories {
mavenLocal()
- jcenter()
mavenCentral()
+ gradlePluginPortal()
}
dependencies {
- classpath "com.ofg:uptodate-gradle-plugin:$uptodateVersion"
- classpath "me.tatarka:gradle-retrolambda:$retrolambdaPluginVersion"
+ classpath "com.github.ben-manes:gradle-versions-plugin:0.42.0"
+ classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.1.0"
+ }
+
+ wrapper {
+ gradleVersion = "7.4"
+ distributionType = Wrapper.DistributionType.ALL
}
}
-apply plugin: "jacoco"
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
@@ -35,26 +32,24 @@ if (JavaVersion.current().isJava8Compatible()) {
allprojects {
-
+ apply plugin: "jacoco"
jacoco {
-// toolVersion = "0.7.1.201405082137"
- toolVersion = "0.7.6.201602180812"
-
+ toolVersion = "0.8.7"
}
defaultTasks "build"
- ext {
+ ext {
isSnapshot = true
- fjBaseVersion = "4.7"
+ fjBaseVersion = "5.1"
snapshotAppendix = "-SNAPSHOT"
fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "")
- fjConsumeVersion = "4.6"
+ fjConsumeVersion = "5.0"
signModule = false
- useRetroLambda = false
+ uploadModule = false
projectTitle = "Functional Java"
projectName = "functionaljava"
@@ -64,27 +59,32 @@ allprojects {
projectUrl = "http://functionaljava.org/"
scmUrl = "git://github.com/functionaljava/functionaljava.git"
scmGitFile = "scm:git@github.com:functionaljava/functionaljava.git"
+ scmSshGitFile = "scm:git:ssh://git@github.com/functionaljava/functionaljava.git"
+ licenseUrl = "https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE"
+ licenseName = "The BSD3 License"
+
+ issueUrl = "https://github.com/functionaljava/functionaljava/issues"
+ githubUrl = "https://github.com/functionaljava/functionaljava"
sonatypeBaseUrl = "https://oss.sonatype.org"
sonatypeSnapshotUrl = "$sonatypeBaseUrl/content/repositories/snapshots/"
sonatypeRepositoryUrl = "$sonatypeBaseUrl/content/groups/public"
sonatypeReleaseUrl = "$sonatypeBaseUrl/service/local/staging/deploy/maven2/"
+
sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl
+
primaryEmail = "functionaljava@googlegroups.com"
- dependencyJunit = "junit:junit:4.12"
+ junitCompile = "junit:junit:4.13.2"
+ junitRuntime = "org.junit.vintage:junit-vintage-engine:5.8.2"
displayCompilerWarnings = true
-
- newJdkEnvVar = "JAVA8_HOME"
- oldJdkEnvVar = "JAVA6_HOME"
- retroLambdaTarget = JavaVersion.VERSION_1_6
+ generateTestReports = false
}
repositories {
- jcenter()
- mavenCentral()
mavenLocal()
+ mavenCentral()
}
version = fjVersion
@@ -97,65 +97,64 @@ subprojects {
buildscript {
repositories {
+ mavenLocal()
mavenCentral()
}
}
- apply plugin: "jacoco"
-
apply from: "$rootDir/lib.gradle"
- apply plugin: "java"
+ apply plugin: "java-library"
apply plugin: "eclipse"
- apply plugin: "com.ofg.uptodate"
repositories {
mavenLocal()
- jcenter()
mavenCentral()
maven {
url sonatypeRepositoryUrl
}
}
- if (displayCompilerWarnings) {
- tasks.withType(JavaCompile) {
+ tasks.withType(JavaCompile) {
+ if (displayCompilerWarnings) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
- }
- }
+ }
+ }
- jacocoTestReport {
- reports {
- html.enabled = true
- xml.enabled = true
+ tasks.withType(Test).configureEach {
+ maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+ if (!generateTestReports) {
+ reports.html.required = false
+ reports.junitXml.required = false
}
}
- task coverage(dependsOn: ["test", "jacocoTestReport"]) << {}
}
task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
- dependsOn = subprojects.coverage
+ dependsOn = subprojects*.test
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
// We only care about coverage of:
- def projectForFoverage = ["core", "java8", "quickcheck", "java-core"]
- classDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.output)
- sourceDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs)
+ def projectForFoverage = ["core", "quickcheck", "java-core"]
+ getClassDirectories().from(files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.output))
+ getSourceDirectories().from(files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs))
reports {
- html.enabled = true
- xml.enabled = true
+ html.required = true
+ xml.required = true
}
}
configure(subprojects.findAll { it.name != "props-core" }) {
- apply plugin: "maven"
+ apply plugin: "maven-publish"
apply plugin: "signing"
- apply plugin: "osgi"
-
+ apply plugin: "biz.aQute.bnd.builder"
sourceCompatibility = "1.8"
+ javadoc {
+ }
+
task javadocJar(type: Jar, dependsOn: "javadoc") {
classifier = 'javadoc'
from "build/docs/javadoc"
@@ -173,16 +172,14 @@ configure(subprojects.findAll { it.name != "props-core" }) {
}
jar {
- version project.fjVersion
- manifest {
- name = 'Functional Java'
- instruction 'Signature-Version', project.fjVersion
- instruction 'Bundle-ActivationPolicy', 'lazy'
- instruction 'Bundle-Vendor', 'functionaljava.org'
- if(project.name != "core") {
- instruction 'Require-Bundle', 'org.functionaljava;bundle-version="'+project.fjBaseVersion+'"'
- }
- }
+ archiveVersion = project.fjVersion
+ bnd (
+ 'Bundle-Name': 'Functional Java',
+ 'Signature-Version': project.fjVersion,
+ 'Bundle-ActivationPolicy': 'lazy',
+ 'Bundle-Vendor': 'functionaljava.org',
+ 'Automatic-Module-Name': "functionaljava${project.name == 'core' ? '' : ".$project.name"}",
+ )
}
eclipse {
@@ -194,7 +191,7 @@ configure(subprojects.findAll { it.name != "props-core" }) {
}
// Output MANIFEST.MF statically so eclipse can see it for plugin development
- task eclipsePluginManifest(dependsOn: jar) << {
+ task eclipsePluginManifest(dependsOn: jar) doLast {
file("META-INF").mkdirs()
jar.manifest.writeTo(file("META-INF/MANIFEST.MF"))
}
@@ -202,6 +199,6 @@ configure(subprojects.findAll { it.name != "props-core" }) {
eclipseProject.dependsOn eclipsePluginManifest
}
-task env << {
+task env doLast {
println System.getenv()
}
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..b7ddeec5
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,4 @@
+ignore:
+ - "demo/.*"
+ - "consume/.*"
+ - "performance/.*"
diff --git a/consume/build.gradle b/consume/build.gradle
index ff92a601..45dadefb 100644
--- a/consume/build.gradle
+++ b/consume/build.gradle
@@ -2,7 +2,8 @@
archivesBaseName = "${project.projectName}-${project.name}"
dependencies {
- compile("$group:$projectName:$fjConsumeVersion")
+ api "$group:$projectName:$fjConsumeVersion"
- testCompile dependencyJunit
+ testImplementation junitCompile
+ testRuntimeOnly junitRuntime
}
diff --git a/consume/src/test/java/fj/EmptyTest.java b/consume/src/test/java/fj/EmptyTest.java
index e112a97a..13675822 100644
--- a/consume/src/test/java/fj/EmptyTest.java
+++ b/consume/src/test/java/fj/EmptyTest.java
@@ -5,9 +5,6 @@
import org.junit.Assert;
-/**
- * Created by MarkPerry on 30/08/2015.
- */
public class EmptyTest {
@Ignore @Test
diff --git a/core/build.gradle b/core/build.gradle
index b3e1756e..282ec191 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -1,19 +1,18 @@
ext {
signModule = true
+ uploadModule = true
+
}
archivesBaseName = project.projectName
dependencies {
- testCompile dependencyJunit
- testCompile 'com.h2database:h2:1.4.191'
- testCompile 'commons-dbutils:commons-dbutils:1.6'
+ testImplementation junitCompile
+ testRuntimeOnly junitRuntime
+ testImplementation 'com.h2database:h2:2.1.210'
+ testImplementation 'commons-dbutils:commons-dbutils:1.7'
}
performSigning(signingEnabled, signModule)
-configureUpload(signingEnabled, signModule)
-
-uploadArchives.enabled = true
-
-configureAllRetroLambda()
+configureUpload(signingEnabled, signModule, uploadModule)
diff --git a/core/src/main/java/fj/Bottom.java b/core/src/main/java/fj/Bottom.java
index a70629f1..5cc7f4e4 100644
--- a/core/src/main/java/fj/Bottom.java
+++ b/core/src/main/java/fj/Bottom.java
@@ -2,8 +2,6 @@
/**
* Represents the bottom _|_ value.
- *
- * @version %build.number%
*/
public final class Bottom {
private Bottom() {
diff --git a/core/src/main/java/fj/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 56222de1..8bee1951 100644
--- a/core/src/main/java/fj/Class.java
+++ b/core/src/main/java/fj/Class.java
@@ -11,8 +11,6 @@
/**
* A wrapper for a {@link java.lang.Class} that provides additional methods.
- *
- * @version %build.number%
*/
public final class Class {
private final java.lang.Class c;
diff --git a/core/src/main/java/fj/Digit.java b/core/src/main/java/fj/Digit.java
index 7213e278..4fec7b0c 100644
--- a/core/src/main/java/fj/Digit.java
+++ b/core/src/main/java/fj/Digit.java
@@ -6,8 +6,6 @@
/**
* The digits zero to nine.
- *
- * @version %build.number%
*/
public enum Digit {
/**
diff --git a/core/src/main/java/fj/Effect.java b/core/src/main/java/fj/Effect.java
index 80680cac..2b5dcd6f 100644
--- a/core/src/main/java/fj/Effect.java
+++ b/core/src/main/java/fj/Effect.java
@@ -14,8 +14,6 @@
/**
* Represents a side-effect.
- *
- * @version %build.number%
*/
public final class Effect {
diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java
index b64c758d..cba4a589 100644
--- a/core/src/main/java/fj/Equal.java
+++ b/core/src/main/java/fj/Equal.java
@@ -1,21 +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.Seq;
-import fj.data.Set;
-import fj.data.Stream;
-import fj.data.Tree;
-import fj.data.TreeMap;
-import fj.data.Validation;
-import fj.data.Writer;
+import fj.data.*;
import fj.data.hamt.BitSet;
import fj.data.hlist.HList;
+import fj.data.optic.Traversal;
import fj.data.vector.V2;
import fj.data.vector.V3;
import fj.data.vector.V4;
@@ -23,6 +11,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;
@@ -33,8 +22,6 @@
/**
* Tests for equality between two objects.
- *
- * @version %build.number%
*/
public final class Equal {
@@ -48,6 +35,39 @@ public interface Definition {
default boolean equal(A a1, A a2) {
return equal(a1).f(a2);
}
+
+ /**
+ * Refine this equal definition, to tests equality of self and the mapped object in "and" manner.
+ * @see #equal()
+ *
+ * @param f The function to map the original object
+ * @param eq Equality for the mapped object
+ * @return A new equal definition
+ */
+ default Definition then(final F f, final Equal eq) {
+ Definition bEqDef = eq.def;
+ return new Definition() {
+ @Override
+ public F equal(A a1) {
+ F fa = Definition.this.equal(a1);
+ F fb = bEqDef.equal(f.f(a1));
+ return a2 -> fa.f(a2) && fb.f(f.f(a2));
+ }
+
+ @Override
+ public boolean equal(A a1, A a2) {
+ return Definition.this.equal(a1, a2) && bEqDef.equal(f.f(a1), f.f(a2));
+ }
+ };
+ }
+
+ /**
+ * Build an equal instance from this definition.
+ * to be called after some successive {@link #then(F, Equal)} calls.
+ */
+ default Equal equal() {
+ return equalDef(this);
+ }
}
/**
@@ -118,18 +138,46 @@ public F eq(final A a) {
* @return A new equal.
*/
public Equal contramap(final F f) {
- Definition eaDef = def;
- return equalDef(new Definition(){
+ 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(eaDef.equal(f.f(b)), f);
+ return compose(aEqDef.equal(f.f(b)), f);
}
@Override
public boolean equal(B b1, B b2) {
- return eaDef.equal(f.f(b1), f.f(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);
}
/**
@@ -307,6 +355,21 @@ public static Equal> eitherEqual(final Equal ea, final Eq
));
}
+ public static Equal> either3Equal(Equal ea, Equal eb, Equal ec) {
+ return equalDef((e1, e2) ->
+ optionEqual(ea).eq(e1.leftOption(), e2.leftOption()) &&
+ optionEqual(eb).eq(e1.middleOption(), e2.middleOption()) &&
+ optionEqual(ec).eq(e1.rightOption(), e2.rightOption())
+ );
+ }
+
+ public static Equal> resultEqual(final Equal ea, final Equal ei) {
+ Definition eaDef = ea.def;
+ Definition eiDef= ei.def;
+ return equalDef((r1, r2) -> eaDef.equal(r1.value(), r2.value()) && eiDef.equal(r1.rest(), r2.rest()));
+ }
+
+
/**
* An equal instance for the {@link Validation} type.
*
@@ -395,6 +458,40 @@ public static Equal> streamEqual(final Equal ea) {
}
/**
+ * An equal instance for the {@link Zipper} type.
+ *
+ * @param ea Equality across the elements of the zipper.
+ * @return An equal instance for the {@link Zipper} type.
+ */
+ public static Equal> zipperEqual(final Equal ea) {
+ Equal> se = Equal.streamEqual(ea);
+ return equalDef((a1, a2) ->
+ se.eq(a1.lefts(), a2.lefts()) &&
+ ea.eq(a1.focus(), a2.focus()) &&
+ se.eq(a1.rights(), a2.rights())
+ );
+ }
+
+ /**
+ * An equal instance for the {@link TreeZipper} type.
+ *
+ * @param ea Equality across the elements of the tree zipper.
+ * @return An equal instance for the {@link TreeZipper} type.
+ */
+ public static Equal> treeZipperEqual(final Equal ea) {
+ final Equal> te = Equal.treeEqual(ea);
+ final Equal>> st = streamEqual(Equal.treeEqual(ea));
+ final Equal>, A, Stream>>>> sp =
+ streamEqual(p3Equal(streamEqual(treeEqual(ea)), ea, streamEqual(treeEqual(ea))));
+ return equalDef((a1, a2) ->
+ te.eq(a1.focus(), a2.focus()) &&
+ st.eq(a1.lefts(), a2.lefts()) &&
+ st.eq(a1.rights(), a2.rights()) &&
+ sp.eq(a1.parents(), a2.parents())
+ );
+ }
+
+ /**
* An equal instance for the {@link Array} type.
*
* @param ea Equality across the elements of the array.
@@ -674,7 +771,7 @@ public static Equal> v8Equal(final Equal ea) {
/**
* An equal instance for lazy strings.
*/
- public static final Equal eq = streamEqual(charEqual).contramap(LazyString.toStream);
+ public static final Equal eq = streamEqual(charEqual).contramap(LazyString::toStream);
/**
* An equal instance for the empty heterogeneous list.
diff --git a/core/src/main/java/fj/F.java b/core/src/main/java/fj/F.java
index e2db316e..1736f97b 100644
--- a/core/src/main/java/fj/F.java
+++ b/core/src/main/java/fj/F.java
@@ -1,12 +1,25 @@
package fj;
+import fj.control.parallel.Actor;
+import fj.control.parallel.Promise;
+import fj.control.parallel.Strategy;
+import fj.data.*;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.TreeSet;
+import java.util.concurrent.*;
+import java.util.function.Function;
+
+import static fj.data.Option.some;
+import static fj.data.Stream.iterableStream;
+import static fj.data.Zipper.fromStream;
+
/**
- * A transformation or function from A to B. This type can be represented
- * using the Java 7 closure syntax.
- *
- * @version %build.number%
+ * A transformation or function from A to B.
*/
-public interface F {
+@FunctionalInterface
+public interface F extends Function {
/**
* Transform A to B.
*
@@ -15,4 +28,668 @@ public interface F {
*/
B f(A a);
+ default B apply(A a) {
+ return f(a);
+ }
+
+ /**
+ * Function composition
+ *
+ * @param g A function to compose with this one.
+ * @return The composed function such that this function is applied last.
+ */
+ default F o(final F g) {
+ return c -> f(g.f(c));
+ }
+
+ /**
+ * First-class function composition
+ *
+ * @return A function that composes this function with another.
+ */
+ default F, F> o() {
+ return g -> o(g);
+ }
+
+ /**
+ * Function composition flipped.
+ *
+ * @param g A function with which to compose this one.
+ * @return The composed function such that this function is applied first.
+ */
+ @SuppressWarnings("unchecked")
+ default F andThen(final F g) {
+ return g.o(this);
+ }
+
+ /**
+ * First-class composition flipped.
+ *
+ * @return A function that invokes this function and then a given function on the result.
+ */
+ default F, F> andThen() {
+ return g -> andThen(g);
+ }
+
+ /**
+ * Binds a given function across this function (Reader Monad).
+ *
+ * @param g A function that takes the return value of this function as an argument, yielding a new function.
+ * @return A function that invokes this function on its argument and then the given function on the result.
+ */
+ default F bind(final F> g) {
+ return a -> g.f(f(a)).f(a);
+ }
+
+ /**
+ * First-class function binding.
+ *
+ * @return A function that binds another function across this function.
+ */
+ default F>, F> bind() {
+ return g -> bind(g);
+ }
+
+ /**
+ * Function application in an environment (Applicative Functor).
+ *
+ * @param g A function with the same argument type as this function, yielding a function that takes the return
+ * value of this function.
+ * @return A new function that invokes the given function on its argument, yielding a new function that is then
+ * applied to the result of applying this function to the argument.
+ */
+ default F apply(final F> g) {
+ return a -> g.f(a).f(f(a));
+ }
+
+ /**
+ * First-class function application in an environment.
+ *
+ * @return A function that applies a given function within the environment of this function.
+ */
+ default F>, F> apply() {
+ return g -> apply(g);
+ }
+
+ /**
+ * Applies this function over the arguments of another function.
+ *
+ * @param g The function over whose arguments to apply this function.
+ * @return A new function that invokes this function on its arguments before invoking the given function.
+ */
+ default F> on(final F> g) {
+ return a1 -> a2 -> g.f(f(a1)).f(f(a2));
+ }
+
+
+
+ /**
+ * Applies this function over the arguments of another function.
+ *
+ * @return A function that applies this function over the arguments of another function.
+ */
+ default F>, F>> on() {
+ return g -> on(g);
+ }
+
+ /**
+ * Promotes this function so that it returns its result in a product-1. Kleisli arrow for P1.
+ *
+ * @return This function promoted to return its result in a product-1.
+ */
+ default F> lazy() {
+ return a -> P.lazy(() -> f(a));
+ }
+
+ /**
+ * Partial application.
+ *
+ * @param a The A to which to apply this function.
+ * @return The function partially applied to the given argument to return a lazy value.
+ */
+ default P1 partial(final A a) {
+ return P.lazy(() -> f(a));
+ }
+
+ /**
+ * Promotes this function to map over a product-1.
+ *
+ * @return This function promoted to map over a product-1.
+ */
+ default F, P1> mapP1() {
+ return p -> p.map(this);
+ }
+
+ /**
+ * Promotes this function so that it returns its result in an Option. Kleisli arrow for Option.
+ *
+ * @return This function promoted to return its result in an Option.
+ */
+ default F> optionK() {
+ return a -> some(f(a));
+ }
+
+ /**
+ * Promotes this function to map over an optional value.
+ *
+ * @return This function promoted to map over an optional value.
+ */
+ default F