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 super T>>>>() {
- public Option, java.lang.Class super T>>> f(
- final java.lang.Class super T> c) {
- if (c == null)
- return none();
- else {
- final P2, java.lang.Class super T>> p =
- new P2, java.lang.Class super T>>() {
- public java.lang.Class super T> _1() {
- return c;
- }
+ (java.lang.Class super T> c2) -> {
+ if (c2 == null)
+ return none();
+ else {
+ final P2, java.lang.Class super T>> p =
+ new P2, java.lang.Class super T>>() {
+ public java.lang.Class super T> _1() {
+ return c2;
+ }
- @SuppressWarnings({"unchecked"})
- public java.lang.Class super T> _2() {
- return c.getSuperclass();
- }
- };
- return some(p);
- }
- }
- }, c).map(new F, Class super T>>() {
- public Class super T> f(final java.lang.Class super T> c) {
- return clas(c);
- }
- });
+ @SuppressWarnings("unchecked")
+ public java.lang.Class super T> _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