Meet Zazr: a Vavr fork bringing modern functional programming to Java¶
Zazr is a library of immutable collections and functional types for Java 25 and later. It is built on what the language now offers: records, sealed interfaces and pattern matching. Its tagline says where the ideas come from: inspired by Scala 2.13+, ZIO, and zio-prelude.
This post says what Zazr is, why we built it, what it borrows and why, and where it stands today.
What Zazr is¶
Zazr gives Java two families of types, made to work together.
Types that say what can happen. Option is a value that may be absent. Either and Try are the result of an
operation that may fail. Validation is a set of checks that reports every error at once. They are sealed interfaces
of records, so a switch takes them apart, and the compiler checks that every case is handled.
Collections that never change. Vector, List, HashMap, TreeSet and the others are immutable: adding or
removing an element returns a new collection, which shares most of its structure with the old one. Every operation
whose cost depends on the size says what that cost is, so you choose a collection for what you do with it.
Some collections can never be empty: NonEmptyVector, NonEmptySet and NonEmptyMap, with their sorted versions.
On them, reduce always returns a value, and an operation that could remove every element, like filter, gives
back a plain collection, so the type always tells you whether emptiness is still ruled out.
Both fit into ordinary Java code. A builder fills a collection in a loop, asJava() hands it to any Java API without
copying, and the collections and the "present" cases (Some, Right, Success, Valid) never hold null. Zazr
has no runtime dependencies.
Why we built it¶
We come from Scala and ZIO. There, we got used to a way of writing code: values that don't change, errors that are values, and names that say what an operation does.
From the outside, functional programming can look like a field that stopped moving: old ideas wrapped in jargon. It isn't. Functional programming keeps evolving, and some of its most useful progress has made it simpler. ZIO did that for Scala: it traded jargon for names that say what they do, and abstractions you had to study for operations you can read. Zazr brings that to Java. Zazr brings modern FP to Java.
Java 25 has the pieces that make this style feel at home: records, sealed interfaces, record patterns and
exhaustive switch. We wanted a library that builds on those pieces, so that functional code in Java reads like
modern Java.
That is Zazr.
What we took, and why¶
Most of Zazr is borrowed. We took the parts we trust from the ecosystems we know, and ported them to Java.
The Scala 2.13 collections library¶
Scala 2.13 rewrote its collections library, and Scala 3 uses that library unchanged. Zazr's Vector, HashSet,
HashMap and LazyList are ported from it, and so are parts of TreeSet and TreeMap:
Vectoris a radix-balanced tree of arrays of 32 elements, so reading, updating, appending and prepending are effectively constant time.HashSetandHashMapare CHAMP hash tries, compact and shared between versions.filterandpartitiononTreeSetandTreeMapuse Scala's red-black tree algorithms, and keep every subtree they don't change.LazyListis fully lazy: building one, or calling a lazy operation on it, evaluates nothing.
The reason: these designs are well studied and have years of use behind them in Scala. So when Zazr has a choice to make about a collection, it starts from what Scala does.
ZIO's names¶
In ZIO, an operation is named after what it does. Combining values is zip. Turning a list of results into a
result of a list is collectAll. Recovering from an error is catchAll. Running a side effect on a value is tap.
Zazr uses those names. You can read the code without learning any theory first, and if you know ZIO, you already know them.
zio-prelude's Validation¶
When you check a form or a configuration, you want every error at once, not one per attempt. zio-prelude's
Validation does that: combining several checks keeps all of their errors, in a collection that is never empty.
Zazr's Validation works the same way. flatMap still stops at the first error, for the step that needs the
previous value.
Scala's Using¶
Using is a port of Scala's scala.util.Using. It releases resources after use, whatever happened, and gives the
outcome as a Try. A Using.manager block can acquire as many resources as it needs, even when their number is
known only at run time.
A taste¶
Here is a signup with two checks. Both fail, and the result keeps both errors:
record Signup(String email, int age) {}
var email = Validation.fromPredicate("jules", e -> e.contains("@"), e -> "email has no @");
var age = Validation.fromPredicate(-1, a -> a >= 0, a -> "age is negative");
var signup = Validation.zipWith(email, age, Signup::new); // Validation<String, Signup>
var message = switch (signup) {
case Valid(var s) -> "welcome, " + s.email();
case Invalid(var errors) -> errors.mkString(", ");
};
// "email has no @, age is negative"
zipWith combines up to eight values in one call. Valid and Invalid are records, so the switch takes the
result apart, and the compiler knows those are the two cases. The Validation page
goes further.
Thank you, Vavr¶
Zazr is a fork of Vavr, and it exists because Vavr exists.
Vavr was created by Daniel Dietrich and is maintained by Grzegorz Piwowarek, with its contributors. Their work gave Zazr its starting point: its collections and its control types. Thank you, all of you.
Zazr has changed a lot since the fork. The Compared to Vavr page lists every difference, for anyone who knows Vavr.
We also want to thank the Scala and ZIO communities. The collections, the names and Validation are their work, and
Zazr builds on it.
Where it stands¶
Zazr 0.1.0 is on Maven Central:
<dependency>
<groupId>dev.zazr</groupId>
<artifactId>zazr-core</artifactId>
<version>0.1.0</version>
</dependency>
It is pre-1.0, and the API may still change between releases. The release notes list every change.
Feedback is very welcome. If something is missing, unclear or broken, please open an issue.
What's next¶
The next posts will each teach one thing Zazr brings, starting with property-based testing with zazr-test.
Until then:
- Getting started takes you from the dependency to your own code.
- Zazr in your AI assistant teaches your coding assistant to write Zazr code.
- The GitHub repository has the code, the issues and the releases.
Thank you for reading.