Type-safe versions of Clojure's immutable/persistent collections, an immutable alternative to Java 8 Streams, and other tools to make functional programming in Java easier.

Classic

You are on the Paguro Classic, or main branch of this project. If you work with a mix of Java and Kotlin files, you may wish to try Paguro-KF 3.5.x in the 2017-09-17b_KotlinFootWetting branch. If you want to live dangerously, try the all-Kotlin version in the 4.0 branch when it becomes available.

Hermit Crab Photo by Rushen

Paguro is short for the Latin "Paguroidea" - the name of the Hermit Crab superfamily in Biology. These collections grow by adding a new shell, leaving the insides the same, much the way Hermit Crabs trade up to a new shell when they grow.

javadoc Build Status Code Coverage

Join the chat at https://gitter.im/GlenKPeterson/Paguro

Maven Artifact

Available from the Maven Repository as:

<!--
If you're using Kotlin and Java, you want the 3.5 version in the KotlinFootWetting branch.
Java-only users want 3.x from the main branch.
 -->
<dependency>
        <groupId>org.organicdesign</groupId>
        <artifactId>Paguro</artifactId>
        <version>3.1.3</version>
</dependency>

The Maven artifact is the easiest way to use Paguro, but you can build from source if you want to.

News

Don't use RrbTree.join()!

@jafingerhut has found bugs in the RRB Tree join implementation. See issue 31 and 36. I normally try to fix bugs promptly, but the issue here is that I don't know how to fix it! I understood a new chunk of the problem last night 2020-02-01 and am probably going to use jafingerhut's algorithm for a solution. If anyone knows of a paper with a working algorithm for merging two n-ary BTrees, let us know!

RRB Tree Released!

An RRB Tree is an immutable List (like Clojure's PersistentVector) that also supports random inserts, deletes, and can be split and joined back together in logarithmic time. Details: https://github.com/GlenKPeterson/Paguro/releases/tag/3.0.16

Next Major Release will be Paguro 4.0, "Kotlin Compatibility"

This announcement is making some people nervous even as it makes others happy. The primary curator (Glen) will still continue using Paguro in both Java and Kotlin for at least a year, maybe forever. Kotlin is nearly 100% backward-compatible with Java 8. I've met several people who know Paguro but not Kotlin, but I have yet to meet the person who knows both and likes Paguro but not Kotlin.

You are probably interested in Paguro because you like Immutability, Functional Programming (maybe as pure as Haskell, maybe not), and Types. Kotlin is a briefer Java that assumes immutability, makes Functional Programming easier, and plugs 3/4 of the quirks in Java's generic type system. If you're concerned about the future of Paguro, I think the best way to put your worries to rest is to try Kotlin. It's pretty great!

If a rewrite in Kotlin sounds good to you, consider voting for this issue because without it, I'll have to maintain separate Java and Kotlin code.

Check back for more details as the 4.x release progresses.

Check the Change Log for details of recent changes.

Features

API Docs

Paguro takes advantage of Java's type inferencing. It eschews void return types, arrays, primatives, and checked exceptions in lambdas. It can decrease the amount of code you need to write by a factor of at 2x-3x. Using functional transfomrations instead of loops focuses you on choosing the right collections which leads to more readable code AND better Big O complexity/scalability.

Details

// Define some people with lists of email addresses on the fly.
// vec() makes a Vector/List, tup() makes a Tuple
vec(tup("Jane", "Smith", vec("[email protected]", "[email protected]")),
    tup("Fred", "Tase", vec("[email protected]", "[email protected]", "[email protected]")))

        // We want to look up people by their address.
        // There are multiple addresses per person.
        // For each person, find their email addresses.
        .flatMap(person -> person._3()

                                 // For each address, produce a key/value pair
                                 // of email and person (Tuple2 implements Map.Entry)
                                 .map(email -> tup(email, person)))

        // toImMap() collects the results to key/value pairs and puts
        // them in an immutable map.  We already have pairs, so pass
        // them through unchanged.
        .toImMap(x -> x)

        // Look up Jane by her address
        .get("[email protected]")

        // Get her first name (returns "Jane")
        ._1();

Manifesto

FAQ

Q: Why are you doing this?

It started with a Software Engineering Stack Exchange question: Why doesn't Java provide immutable collections?

Q: How does this compare to PCollections?

Paguro is based on Clojure, faster and has additional features

Q: Do these Transforms create intermediate collections between each operation (like the Scala collections)?

No

Q: How does this compare to Streams and lambda expressions in JDK8?

Comparison

Q: Why Java instead of another/better JVM language?

Why Java? That said, this could become a Kotlin-based project.

Future Development Ideas (as of 2017-09-10)

  1. Make all collections sub-classes of Kotlin's collections
  2. Add a fast ListIterator to RRB implementation. I have made some strides toward this, but it's slow work, PersistentVector never got this feature, and Kotlin compatibility is a higher priority.
  3. All methods of Xform can be implemented in terms of foldUntil(). Try doing that instead of _fold.
  4. Ensure everything is as friendly as possible to Monadic and Reactive thinking.
  5. Consider a Fn1v subclass of Fn1 (and similar for Fn0, Fn2, etc.) that returns void because sometimes you need one of those for backward compatibility and you don't want it to choke on checked exceptions.
  6. Consider insertion-order maps and sets

Licenses

Java™ is a registered trademark of the Oracle Corporation in the US and other countries. Paguro is not part of Java. Oracle is in no way affiliated with the Paguro project.

Paguro is not part of Clojure. Rich Hickey and the Clojure team are in no way affiliated with the Paguro project, though it borrows heavily from their thoughts and is partly a derivative work of their open-source code.

The Clojure collections are licensed under the Eclipse Public License. Versions of them have been included in this project and modified to add type safety and implement different interfaces. These files are still derivative works under the EPL.

Unless otherwise stated, the rest of this work is licensed under the Apache 2.0 license. New contributions should be made under the Apache 2.0 license whenever practical. I believe it is more popular, clearer, and has been better tested in courts of law.

Build from Source

The pre-built maven artifact is the easiest way to use Paguro. Mose users do not need to build Paguro from source.

Prerequisites

Paguro should build on Ubuntu 16.04 and later with openjdk-8-jdk, git, and maven installed from the official repositories. A compiler bug in javac 1.8.0_31 prevents building Paguro, but OpenJDK 1.8.0.91 and later (or Oracle) should work on Windows or Mac.

Environment Variables

Depending on how you installed Java and Maven, you may need to set some of the following in your ~/.profile file and reboot (or source that file like . ~/.profile from the command line you will use for the build). Or do whatever Windows does. If your tools are installed in different directories, you will have to fix the following:

export JDK_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export JAVA_HOME=$JDK_HOME/jre
export M2_HOME=$TOOLS/apache-maven-3.3.9/
export M2="$M2_HOME"bin
export PATH=$PATH:$M2
Build
# Start in an appropriate directory

# You need TestUtils for Paguro's equality testing.
# The first time you build, get a local copy of that and Paguro
git clone https://github.com/GlenKPeterson/TestUtils.git
git clone https://github.com/GlenKPeterson/Paguro.git

# Build TestUtils:
cd TestUtils
git pull
mvn clean install
cd ..

# Build Paguro:
cd Paguro
git pull
mvn clean install
cd ..

Contributing

If you submit a patch, please:

More

Additional information is in: README2.md.