Skip to content
Immutable Collections and Functional Transformations for the JVM
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.idea/copyright Converted another unit test from ScalTest to JUnit and fixed copyrigh… Mar 7, 2014
docs Updated JavaDoc yet again. Mar 28, 2018
paguro-bench Improved benchmarks so they reuse the same 16 Integers instead of box… Jun 11, 2017
src Made most Cowry methods public Feb 22, 2019
tupleGenerator Regenerated all tuples and updated Usage Example. Sep 6, 2016
.gitignore Updated to-do list and feature list goal for 1.0 release. Apr 6, 2015
.travis.yml More changes to travis file. Oct 4, 2015
CHANGE_LOG.md Made most Cowry methods public Feb 22, 2019
CodeStylePaguro.xml These are UNTESTED changes, saved so I can switch branches. Sep 19, 2016
LICENSE.txt Update LICENSE.txt Jun 21, 2015
Paguro.ipr Merged changes from master branch. Sep 17, 2016
README.md Made most Cowry methods public Feb 22, 2019
README2.md Update README2.md Oct 29, 2017
epl-v10.html
inheritanceHierarchy.odg
inheritanceHierarchy.pdf Added custom serialization for PersistentHashMap. Changed PersistentH… Sep 4, 2016
inheritanceHierarchy3.odg Fixed some comments. May 20, 2017
logBase2VsLogBase32.png Added FAQ and fixed errors in docs based on questions received. Sep 23, 2015
pom.xml Made most Cowry methods public Feb 22, 2019
updateJavadoc.sh Added BaseMap, BaseSet, and BaseUnsortedMap to provide fluent interfa… May 31, 2017

README.md

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.

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

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

  • Immutable collections api/src - type-safe generic Java versions of Clojure's immutable (HAMT = 'Hash Array Mapped Trie') collections - arguably the best immutable collections on the JVM. Plus an RRB Tree!
  • Functional transformations api/src are like a type-safe version of Clojure's Transducers, or a simplified immutable alternative to Java 8 Streams, wrapping checked exceptions and avoiding primitives (you can still use Java 8 streams if you want to).
  • Brief collection constructors api/src are like a tiny, type-safe data definition language:
    • vec("one", "two", "three") - an immutable vector/list of three strings
    • set(3, 5, 7) - an immutable set of three integers
    • tup("Alice", 11, 3.14) - an immutable 3-field tuple or record
    • map(tup(1, "single"), tup(2, "double"), tup(3, "triple")) - an immutable map that uses integers to look up appropriate strings.
  • Extensible, immutable tuples api/src - use them for rapid prototyping, then later extend them to make your own lightweight, immutable Java classes with correct equals(), hashCode(), and toString() implementations.
  • Lazy initialization api/src - LazyRef thread-safely performs initialization and frees initialization resources on first use. Subsequent uses get the now-constant initialized value. Use this instead of static initializers to avoid initialization loops. Cache results of expensive operations for reuse.
  • Union types api/src - Not as nice as being built into the language, but they extend type safety outside the object hierarchy.
  • Memoization api/src - Turns function calls into hashtable lookups to speed up slow functions over a limited range of inputs.
  • Tiny with no dependencies - The entire project fits in a 270K jar file that is compiled in the compact1 profile.

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("a@b.c", "b@c.d")),
    tup("Fred", "Tase", vec("c@d.e", "d@e.f", "e@f.g")))

        // 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("b@c.d")

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

Manifesto

  • Immutability promotes correct code as much as type safety does.
  • Better to focus on picking the appropriate collections and transformations than on looping details.
  • Write functions before defining classes, yet still take advantage of type safety.
  • On-the fly data definition should be simple and easy. Naming/formalizing those data structures should be too.
  • Minimal, easy-to-understand interface covering the most critical building blocks for higher functionality.

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:

  • Keep the changes minimal (don't let your IDE reformat whole files).
  • Try to match the code style as best you can.
  • Clearly document your changes.
  • Update the unit tests to clearly and simply prove that your code works.
  • It's a good idea to discuss proposed changes by opening an issue on github before you spend time coding.

More

Additional information is in: README2.md.

You can’t perform that action at this time.