Build lists in mathematical set-builder notation with Java, like { x * 2 | x E {1,2,3,4} ^ x is even }.
We may solve our problems using for
+if
statements in an imperative way, or by the usage of map
+filter
if we want to use a functional approach, but we have another powerful way of solving these same problems: list comprehensions. List comprehensions are syntatic constructions that are used to build lists in many languages, such as Haskell, Python or Ruby, using the algebraic set-builder notation. This notation hasn't yet been added to the Java world, and is the intention of this library to do that.
For example, let's suppose we want to get all the even numbers of a given list. In the algebra world we would so with the set-builder notation like
{ x | x E R ^ x is-even }
In Java we may do
List<Integer> evens = new ArrayList<>();
for (int n : list) {
if (n % 2 == 0) {
evens.add(n);
}
}
or
Predicate<Integer> isEven = x -> x % 2 == 0;
List<Integer> evens = list.stream().filter(isEven).collect(Collectors.toList());
These two approaches solve our problem, but now we can also do this with the List Comprehensions approach of jComprehension
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(list);
x.holds(isEven);
});
In order to build a List as a List Comprehension you must instantiate a new ListComprehension
object and call the suchThat
method with the predicates you want the list to hold.
The ListComprehension
object exposes the following methods:
List<T> suchThat(Consumer<Var> predicates)
: Builds the list of elements with the given predicates. See below to check the methods theVar
object exposes.List<Pair<T, T>> suchThat(BiConsumer<Var, Var> predicates)
: Builds the list of pairs of elements with the given predicates. See below to check the methods theVar
object exposes.ListComprehension<T> outputExpression(Function<T, T> resultTransformer)
: Sets an output expressions that will be applied to each element of the resultant list after validating the predicates.ListComprehension<T> outputExpression(BiFunction<T, T, ?> resultTransformer)
: Sets an output expressios that will be applied to each pair of elements of the resultant list after validating the predicates.
When defining the predicates, we have to use a Var
object in a lambda (Predicate
or BiPredicate
).
The Var
object exposes the following methods:
Var belongsTo(List<T> c)
: Defines the list where the elements belong to.Var holds(Predicate<T> p)
: Defines a predicate that each element of the resultant list should hold.Var is(Predicate<T> p)
: Alias of theVar holds(Predicate<T> p)
method.Var holds(BiPredicate<T, T> p)
: Defines a predicate that each pair of elements of the resultant list should hold.Var is(BiPredicate<T, T> p)
: Alias of theVar holds(BiPredicate<T, T> p)
method.
Filter a list of even numbers:
// { x | x E {1,2,3,4,5} ^ x is-even }
Predicate<Integer> isEven = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(s -> {
s.belongsTo(Arrays.asList(1,2,3,4,5));
s.holds(isEven);
});
Map a list of numbers in order to have them duplicated:
// { x*2 | x E {1,2,3,4,5} }
List<Integer> duplicated = new ListComprehension<Integer>()
.outputExpression((Integer x) -> x * 2)
.suchThat(s -> {
s.belongsTo(Arrays.asList(1,2,3,4,5));
});
Make the cartesian product of two lists and keep the pair of elements that hold that an element is the 2 times the other one:
// { (x,y) | x E {1,2,3} ^ y E {4,5,6} ^ 2 * x = y }
List<Pair<Integer,Integer>> doublePairs = new ListComprehension<Integer>()
.suchThat((x, y) -> {
x.belongsTo(Arrays.asList(1,2,3));
y.belongsTo(Arrays.asList(4,5,6));
x.holds((x, y) -> x * 2 == y);
});
My college project of Functional Programming at ITBA (Buenos Aires Institute of Technology): https://github.com/farolfo/list-comprehensions-in-java
- Set-builder notation.
- How are programming languages supporting this today?
- Haskell's list comprehension
MIT