Bring Scala's for-expression syntax to Java. Compose Optional, Stream, and Iterable without deep nesting. Eager and lazy evaluation with optional filter/guard clauses.
Flatten nested monadic chains into clean, readable comprehensions — the way Scala does it.
Brings Scala's for-expression syntax to Java, eliminating deeply nested flatMap and map chains.
Supports Optional, Stream, and Iterable out of the box, with consistent semantics across all container types.
Eager mode for independent values provided upfront; lazy mode when later bindings depend on earlier ones.
Optional .filter() step that acts as a guard clause. Multiple calls are AND-composed automatically.
Chain bindings naturally and terminate with .yield() to produce the result. Clean and readable at any arity.
Compatible with Java 8 and above. No exotic runtime requirements, no virtual threads, no module system.
All examples use import static com.pivovarit.forc.ForComprehension.forc;
// Eager: all values known upfront
Optional<Integer> width = Optional.of(3);
Optional<Integer> height = Optional.of(4);
Optional<Integer> area = forc(width, height)
.yield((w, h) -> w * h);
// area -> Optional[12]
// Lazy: second value depends on first
Optional<Integer> area2 = forc(
Optional.of(3),
w -> Optional.of(w + 1)
).yield((w, h) -> w * h);
// area2 -> Optional[12]
// Eager: cartesian product
Stream<Integer> result = forc(
Stream.of(1, 2),
Stream.of(10, 20)
).yield(Integer::sum);
// result -> [11, 21, 12, 22]
// Lazy: second stream depends on first
Stream<Integer> result2 = forc(
Stream.of(1, 2),
v1 -> Stream.of(v1 * 10, v1 * 20)
).yield(Integer::sum);
// result2 -> [11, 21, 22, 42]
// Eager
List<Integer> result = forc(
List.of(1, 2),
List.of(10, 20)
).yield(Integer::sum);
// result -> [11, 21, 12, 22]
// Lazy
List<Integer> result2 = forc(
List.of(1, 2),
v1 -> List.of(v1 * 10, v1 * 20)
).yield(Integer::sum);
// result2 -> [11, 21, 22, 42]
// Optional — guard failure produces empty()
Optional<Integer> area = forc(
Optional.of(4), Optional.of(3)
).filter((w, h) -> w > h)
.yield((w, h) -> w * h);
// area -> Optional[12] (4 > 3, guard passes)
// Stream — guard filters the cartesian product
Stream<String> allowed = forc(
Stream.of("admin", "user"),
Stream.of("read", "write")
).filter((role, scope) -> !(role.equals("user") && scope.equals("write")))
.yield((role, scope) -> role + ":" + scope);
// allowed -> ["admin:read", "admin:write", "user:read"]
Static factory method forc() on ForComprehension — inspired by Scala's for/yield expressions.
Eager overload. All values are provided upfront. The result type mirrors the container (Optional → Optional, Stream → Stream, Iterable → List).
Lazy overload. Later values are functions of the preceding ones, enabling dependent bindings where each step can reference earlier results.
Guard clause. Keeps only combinations where the predicate holds. Multiple .filter() calls are AND-composed automatically.
Terminal operation. Applies the mapping function to all bound values and produces the final result: Optional<R>, Stream<R>, or List<R>.
// All types use the same forc() + .yield() pattern
// Optional → Optional<R>
Optional<String> r1 = forc(Optional.of("a"), Optional.of("b"))
.yield((a, b) -> a + b);
// Stream → Stream<R> (cartesian product)
Stream<String> r2 = forc(Stream.of("x", "y"), Stream.of("1", "2"))
.yield((a, b) -> a + b);
// Iterable → List<R>
List<String> r3 = forc(List.of("x", "y"), List.of("1", "2"))
.yield((a, b) -> a + b);
<dependency>
<groupId>com.pivovarit</groupId>
<artifactId>for-comprehension4j</artifactId>
<version>0.0.1</version>
</dependency>
implementation 'com.pivovarit:for-comprehension4j:0.0.1'