Skip to content

Releases: guizmaii-opensource/zazr

Release list

Zazr 0.1.0

Choose a tag to compare

@guizmaii guizmaii released this 28 Sep 08:48
f509c3a

Zazr 0.1.0

Website: zazr.dev

The first release.

Zazr is a fork of the latest Vavr, its main branch as of September 2026 (later than the 1.0.1 release), redesigned for Java 25: its collections come from the Scala 2.13 collections library (which Scala 3 uses unchanged), and its names and most control types from ZIO and zio-prelude (Using is a port of Scala's scala.util.Using). It is not a drop-in replacement: the package is dev.zazr, the Maven group is dev.zazr, and much of the API changed on purpose. The entries below compare Zazr 0.1.0 with Vavr.

  • Artifacts: dev.zazr:zazr-core and dev.zazr:zazr-test.
  • JDK 25 or later. No runtime dependencies.

Useful links

Added

Control types

  • Validation keeps every error: its error side is a NonEmptyVector<E>, and zip accumulates errors without changing the type. collectAll, forEach and partition validate a whole collection; fromPredicate builds a check whose error can name the rejected value; flatMapEither chains a step that returns an Either.
  • zip, zipWith, zipLeft and zipRight on Option, Either, Try, Validation and Lazy.
  • Static zip and zipWith taking 2 to 8 values at once, such as Validation.zipWith(name, age, email, User::new), with no nested tuples.
  • Static flatten on every control type, such as Option.flatten(Option<Option<A>>).
  • Try.fromCompletableFuture and Try.toCompletableFuture.

Using

  • Using.of(resource, block) runs a block with a resource and returns a Try.
  • Using.manager(block) handles any number of resources, known only at run time. Resources are released in reverse order, each once, even when an acquisition fails.
  • A value that is not an AutoCloseable, such as a held lock, is registered with its own release action.
  • An error thrown while releasing is never lost: the more severe one surfaces and the other is attached to it as suppressed.

NonEmptyVector

  • A new collection that always holds at least one element, with the whole Vector API.
  • head, last, max, min and reduce cannot fail, since there is no empty case.
  • Operations that keep at least one element (map, append, sorted...) return a NonEmptyVector. Operations that can remove every element (filter, tail, take...) return a Vector.
  • Vector.toNonEmptyVector() and NonEmptyVector.fromIterable(...) narrow to it, returning an Option.
  • NonEmptySet, NonEmptySortedSet, NonEmptyMap and NonEmptySortedMap, on the same model, over HashSet, TreeSet, HashMap and TreeMap. groupBy and toMap on a non-empty collection return a NonEmptyMap.

Collections

  • partitionMap on the sequences and the hash sets: one pass, with a function that returns an Either.
  • duplicates and duplicatesBy on the sequences: the elements that occur more than once.
  • Static flatten on every collection except the maps.
  • putAll on the maps: many entries at once, the argument winning on a shared key, as Scala's ++ does. merge keeps the receiver's entries.
  • Typed sumInt, sumLong, sumDouble, productInt, productLong, productDouble and average on the sequences and sets, each taking the function that reads the number from an element. The int and long forms give the exact result or throw ArithmeticException when it doesn't fit, whatever the iteration order.
  • Positional operations (head, last, take, drop, sliding...) on the ordered sets and maps: LinkedHashSet and LinkedHashMap in insertion order, TreeSet and TreeMap in sorted order.
  • Every operation whose cost depends on the size documents it in its javadoc. The complexity page lists them all.

Builders

  • Vector.Builder, List.Builder, HashMap.Builder, HashSet.Builder, LinkedHashMap.Builder, LinkedHashSet.Builder, TreeMap.Builder and TreeSet.Builder: fill a collection in place, then get it once with result(). Building in a loop no longer copies the collection at each step.
  • ofAll, ofEntries and collector() of these types use their builder.

Java interop

  • asJava() on every collection: a read-only java.util view made in constant time, with no copy. The sequences give a java.util.List, HashSet a Set, LinkedHashSet a SequencedSet and TreeSet a NavigableSet.
  • asJavaMap() on the maps: a java.util.Map, a SequencedMap for LinkedHashMap, a NavigableMap for TreeMap.
  • ofAll given such a view returns the original collection without copying.

zazr-test

  • A new artifact for property-based testing, after zio-test, that works inside any test framework, such as JUnit. The module is dev.zazr.test, and it depends on no test framework.
  • One Gen type with growing sizes, finite generators and a seeded, replayable run; no shrinking. Check.check(gen, ...) fails the test itself with the counterexample, the sample number and the seed; Check.evaluate returns a CheckResult you can pattern match on (Satisfied, Falsified, Erroneous).
  • Assertion: composable checks (equalTo, isGreaterThan, hasSize, contains, isSome, isRight, isValid, throwsA...) that explain why they failed, used as assertThat(value, assertions...) or Check.check(gen, assertions...). AssertJ or JUnit assertions work as bodies too.
  • Generators for every Zazr type, including unusual shapes: vectors built by dropping a prefix, sets and maps that went through removals, lazy lists with an unevaluated tail.
  • Law suites that check the equality, map, flatMap, zip, builder and collection rules of your own types, and name the law that failed.

Documentation

  • The website zazr.dev: getting started, a page per control type and per collection, the complexity page, zip, builders, Java interop, testing, a page for readers new to functional programming, and the comparison with Vavr.
  • An Agent Skill for coding assistants, in the open Agent Skills format: copy skills/zazr into your assistant and it writes idiomatic Zazr code. The AI assistant page says how to install it. Its examples are compiled and run by the build, like the website's.

Changed

Whole library

  • Java 25 is the minimum. The module is dev.zazr and exports dev.zazr, dev.zazr.collection and dev.zazr.control.
  • No null inside. Some, Right, Success, Valid and every collection reject it with a NullPointerException. Option.some(null) throws; use Option.ofNullable. A function that returns null where a value is needed (a flatMap mapper, a classifier, a supplier) fails at once, with a message naming the method.
  • Operations use the JDK's functional interfaces: Supplier, Function, BiFunction, Callable. Function3 to Function8 and CheckedFunction1 to CheckedFunction8 remain, since the JDK has nothing at those arities or with checked exceptions.
  • Names say what an operation does, following ZIO: ap becomes zip/zipWith, sequence becomes collectAll, traverse becomes the static forEach, bimap becomes mapBoth, peek becomes tap, and on the sequences and sets mapTo becomes as. The Compared to Vavr page has the full table.
  • The javadoc is written in Markdown.

Control types

  • Option, Either, Try and Validation are sealed interfaces of records (Some/None, Left/Right, Success/Failure, Valid/Invalid), so pattern matching on them is checked by the compiler.
  • They are no longer Iterable, and they no longer share a common supertype with the collections. Each has a short set of conversions: toOption(), toEither(), toTry(...), toValidation(...), toVector()...
  • Try: recover and recoverWith become catchAll, catchSome, catchAllWith and catchSomeWith; mapFailure becomes mapError; andFinally becomes ensuring; onFailure becomes tapError. Try.of takes a Callable.
  • Two Try.Failures are equal when they hold the same Throwable instance, not when their stack traces match.
  • Either: swap becomes flip, peekLeft becomes tapLeft, and Either.collectAll stops at the first Left. To collect every error, use Validation.
  • Validation.cond and Either.cond become fromPredicate, whose error function receives the rejected value.

Collections

  • Each collection is a concrete type that declares its own API and returns its own type: Vector.map returns a Vector, List.filter a List. The sequences share only Traversable, which holds what every collection does at the same cost (size, contains, exists, foldLeft, mkString, stream()...). The sets also share Set (and SortedSet for TreeSet), the maps Map (and SortedMap for TreeMap).
  • List is a sealed interface of two records, Cons and Nil, so you can pattern match on its head and tail.
    ...
Read more