functional for Java
Four small types you'd otherwise re-implement in every project:
Result, Validated, NonEmptyList,
Retry. Java 21+. Zero runtime dependencies. A ligature's worth
of library: two things, functional types and plain Java, set as one glyph.
This example is a test. It ran, and passed, before this page was built:
Validated<String, Form> form = Validated.accumulate(acc -> {
var name = acc.on(nonBlank("name", ""));
var email = acc.on(nonBlank("email", "ada@analytical.engine"));
var age = acc.on(validAge(-3));
return new Form(name.value(), email.value(), age.value());
});
assertEquals(
Validated.invalid(NonEmptyList.of(
"name must not be blank",
"age must be positive, was -3")),
form);
Result: errors as values
A sealed Ok | Err sum type. Pattern-match failures instead of throwing them.
Validated: every error, at once
Like Result, but Invalid accumulates all errors instead of stopping at the first.
NonEmptyList: emptiness, unrepresentable
A list the compiler knows has at least one element, so head() is total.
Retry: backoff without a framework
A policy-driven retry loop over Result, built for virtual threads.
Putting it together: an order, end to end
Result, Validated, and NonEmptyList meeting in one pipeline, raw input to response.
// Gradle
implementation("dev.fforj:fforj:0.2.0")
Or copy the source of the types you need into your project: every class stands alone by design, and behaves identically either way. The API reference is the full Javadoc.
- No IO monad. Virtual threads removed the practical need; the pure effect tracking that remains costs more than it pays in Java.
- No tuples. Records exist.
- No collections library. The stdlib is fine.
- No type classes. Java's type system doesn't want them.
The surface area is bounded on purpose. This library will not grow into the thing it replaces.