Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

DZone's Guide to

# Introducing Combinators (Part 2)

### As we continue our journey through combinators, we focus on the Provided, Precondition, or Postcondition combinators and see them in action.

· Java Zone ·
Free Resource

Comment (0)

Save
{{ articles[0].views | formatCount}} Views

FlexNet Code Aware, a free scan tool for developers. Scan Java, NuGet, and NPM packages for open source security and open source license compliance issues.

This issue of The Bounds of Java Newsletter is the second part of a series about combinators. Here we introduce additional combinators and show how they can be implemented by building on some combinators presented in the previous newsletter.

## More Combinators

In the previous part of this series, I introduced the Before, After, and Around combinators. Let's go on and show more combinators and how we can use them.

Disclaimer: The combinators introduced below are inspired and based on the excellent Method Combinators library written by Reginald «raganwald» Braithwaite. Please check his page and work for further reference.

### Provided

This is one of the most useful combinators I've ever seen. Look at the `create` method carefully:

``````@FunctionalInterface
public interface Provided<T, R>
extends Function<Predicate<T>,
Function<Function<T, R>,
Function<Function<T, R>,
Function<T, R>>>> {

static <T, R> Provided<T, R> create() {
return condition -> function -> fallback ->
arg -> (condition.test(arg) ? function : fallback).apply(arg);
}

static <T, R> Function<T, R> decorate(
Predicate<T> condition,
Function<T, R> function,
Function<T, R> fallback) {
return Provided.<T, R>create().apply(condition).apply(function).apply(fallback);
}
}``````

This combinator receives the `condition` predicate, the original `function` to be decorated and a `fallback` function, and returns a decorated function. The argument and return types of all `function`, `fallback`, and the result function match, while the type of the `condition` predicate's argument matches the type of the original `function`'s argument.

It works by choosing either the original or the fallback function, based on the result of the condition. First, the `condition` predicate is tested against the `arg` argument, which is the argument of the original `function`. Then, if the test on `condition` returns `true`, the original `function` is selected, whereas if the test returns `false`, the `fallback` function is selected. Thus, the result function is a function that selects one function out of two based on a condition, and applies its argument to the selected function.

The following example shows how to use the `Provided` combinator:

``````class ProvidedExample {

void demo() {
System.out.println("------------------------------------");
System.out.println("Starting PROVIDED combinator demo...");
System.out.println("------------------------------------");

System.out.println("Done - Result is " + result1);
System.out.println("------------------------------------");

System.out.println("Done - Result is " + result2);
System.out.println("------------------------------------");
}

private boolean isTaxable(BigDecimal argument) {
boolean condition = argument.compareTo(BigDecimal.TEN) >= 0; // argument >= 10
System.out.println("PROVIDED: Argument is " + argument + ", condition is " + condition);
return condition;
}

System.out.println("Adding heavy taxes to poor citizen...");
return "\$" + amount.multiply(new BigDecimal("1.22"));
}

private String fallback(BigDecimal amount) {
System.out.println("Fallback: tax exemption");
return "\$" + amount;
}
}``````

Here `this::addTax` represents our original function and `this::isTaxable` acts as our condition predicate, while `this::fallback` is the function that will be executed if the condition is not satisfied. The logic is to execute the original function if the argument of the decorated function is greater than or equal to `10`, otherwise execute the fallback function. Here's the output:

``````------------------------------------
Starting PROVIDED combinator demo...
------------------------------------
PROVIDED: Argument is 100, condition is true
Adding heavy taxes to poor citizen...
Done - Result is \$122.00
------------------------------------
PROVIDED: Argument is 5, condition is false
Fallback: tax exemption
Done - Result is \$5
------------------------------------``````

This output shows that, when called with an argument of `100`, a terrifying `22%` tax is applied, while, when called with an argument of `5`, the amount remains tax free.

The `Provided` combinator can be used almost everywhere... Any time the execution of some code depends on the successful evaluation of a condition, it is a good opportunity to use this combinator.

### Precondition

This combinator is very useful too. It can be seen as a variant of the `Provided` combinator, in which we throw an exception instead of executing a fallback function. Here's the code:

``````@FunctionalInterface
public interface Precondition<T, R, X extends RuntimeException>
extends Function<Predicate<T>,
Function<Function<T, R>,
Function<Function<T, X>,
Function<T, R>>>> {

static <T, R, X extends RuntimeException> Precondition<T, R, X> create() {
return condition -> function -> error -> Provided.decorate(
condition,
function,
arg -> {
throw error.apply(arg);
});
}

static <T, R, X extends RuntimeException> Function<T, R> decorate(
Predicate<T> condition,
Function<T, R> function,
Function<T, X> error) {
return Precondition.<T, R, X>create().apply(condition).apply(function).apply(error);
}
}``````

The implementation of this combinator delegates to the `Provided` combinator's `decorate` method. It receives the `condition` predicate, the original `function` to be decorated and an `error` function, and returns a decorated function. The function that is passed as a fallback to the `Provided` combinator is implemented by throwing the exception returned by the `error` function. The logic is the one of the `Provided` combinator: If the `condition` predicate is not satisfied, the fallback function will be executed, i.e. the exception returned by the `error` function will be thrown.

Take a look at the following example to see how useful the `Precondition` combinator can be:

``````class PreconditionExample {

void demo() {
System.out.println("----------------------------------------");
System.out.println("Starting PRECONDITION combinator demo...");
System.out.println("----------------------------------------");

this::isGreaterThanZero,
NonPositiveAmountTaxException::new);

System.out.println("Done - Result is " + result1);
System.out.println("----------------------------------------");

try {
System.out.println("Done - Result is " + result2);

} catch (NonPositiveAmountTaxException e) {

System.out.println("Exception: " + e.getMessage());
}
System.out.println("----------------------------------------");
}

private boolean isGreaterThanZero(BigDecimal argument) {
boolean condition = argument.compareTo(BigDecimal.ZERO) > 0; // argument > 0
System.out.println("PRECONDITION: Argument is " + argument + ", condition is " + condition);
return condition;
}

System.out.println("Adding heavy taxes to poor citizen...");
return "\$" + amount.multiply(new BigDecimal("1.22"));
}
}

public class NonPositiveAmountTaxException
extends RuntimeException {

private NonPositiveAmountTaxException(String message) {
super(message);
}

public NonPositiveAmountTaxException(BigDecimal amount) {
this("Amount to be taxed must be > 0 but was " + amount);
}
}``````

Again, `this::addTax` represents our original function. The predicate condition, though, is given by the `this::isGreaterThanZero` method, while `NonPositiveAmountTaxException::new` is a constrcutor reference that acts as the error function (note that the public constructor of `NonPositiveAmountTaxException` expects an argument of the same type as the original function). The logic is to execute the original function if the argument of the decorated function is greater than `0`, otherwise throw `NonPositiveAmountTaxException`. Here's the output:

``````----------------------------------------
Starting PRECONDITION combinator demo...
----------------------------------------
PRECONDITION: Argument is 10, condition is true
Adding heavy taxes to poor citizen...
Done - Result is \$12.20
----------------------------------------
PRECONDITION: Argument is -5, condition is false
Exception: Amount to be taxed must be > 0 but was -5
----------------------------------------``````

This output shows that, when called with an argument of `10`, a `22%` tax is applied, while, when called with an argument of `-5`, a `NonPositiveAmountTaxException` is thrown.

The `Precondition` combinator can be used whenever a runtime exception must be thrown if a condition is not met before executing some code.

### Postcondition

If there exists a `Precondition` combinator, there must exist a `Postcondition` one. Let me introduce it to you:

``````@FunctionalInterface
public interface Postcondition<T, R, X extends RuntimeException>
extends Function<Function<T, R>,
Function<BiPredicate<T, R>,
Function<BiFunction<T, R, X>,
Function<T, R>>>> {

static <T, R, X extends RuntimeException> Postcondition<T, R, X> create() {
return function -> condition -> error -> After.decorate(
function,
(argument, result) -> {
if (!condition.test(argument, result)) {
throw error.apply(argument, result);
}
});
}

static <T, R, X extends RuntimeException> Function<T, R> decorate(
Function<T, R> function,
BiPredicate<T, R> condition,
BiFunction<T, R, X> error) {
return Postcondition.<T, R, X>create().apply(function).apply(condition).apply(error);
}
}``````

This combinator is implemented by means of delegation to the `After` combinator's `decorate` method. It receives the original `function` to be decorated, a `condition` predicate and an `error` function, and returns a decorated function. After the original `function` is executed, a `BiFunction` that receives both the original function's `argument` and its `result` is executed. This `BiFunction` checks the condition `BiPredicate` and throws the exception returned by the `error` function if the condition hasn't been met.

Here's an example that shows how the `Postcondition` combinator can be used:

``````class PostconditionExample {

void demo() {
System.out.println("-----------------------------------------");
System.out.println("Starting POSTCONDITION combinator demo...");
System.out.println("-----------------------------------------");

this::checkResultStartsWith\$,
InvalidTaxResultFormatException::new);

System.out.println("Done - Result is " + result1);
System.out.println("-----------------------------------------");

try {
this::checkResultStartsWith\$,
InvalidTaxResultFormatException::new);

System.out.println("Done - Result is " + result2);

} catch (InvalidTaxResultFormatException e) {

System.out.println("Exception: " + e.getMessage());
}
System.out.println("-----------------------------------------");
}

System.out.println("Adding heavy taxes to poor citizen...");
return "\$" + amount.multiply(new BigDecimal("1.22"));
}

System.out.println("Incorrectly adding heavy taxes to poor citizen...");
return "€" + amount.multiply(new BigDecimal("1.22"));
}

private boolean checkResultStartsWith\$(BigDecimal argument, String result) {
boolean condition = result.startsWith("\$");
System.out.println("POSTCONDITION: Argument is " + argument +
", result is " + result + ", condition is " + condition);
return condition;
}
}

public class InvalidTaxResultFormatException
extends RuntimeException {

private InvalidTaxResultFormatException(String message) {
super(message);
}

public InvalidTaxResultFormatException(BigDecimal amount, String formatted) {
this("Result of adding tax to amount " + amount +
" has incorrect format: " + formatted);
}
}``````

The `this::addTax` method reference again represents our original function, which in this case returns a correctly formatted result. We're using the `this::addTaxIncorrect` method reference as an example of function that doesn't satisfy our postcondition. Precisely, the predicate for this postcondition is given by the `this::checkResultStartsWith\$` method reference, while `InvalidTaxResultFormatException::new` is a constructor reference that acts as the error function (note that the public constructor of `InvalidTaxResultFormatException` expects arguments that match the original function's argument and result types). The logic is to execute the original function and then check if its result starts with the `\$` sign. If it doesn't, an `InvalidTaxResultFormatException` is thrown. Here's the output:

``````-----------------------------------------
Starting POSTCONDITION combinator demo...
-----------------------------------------
Adding heavy taxes to poor citizen...
POSTCONDITION: Argument is 10, result is \$12.20, condition is true
Done - Result is \$12.20
-----------------------------------------
Incorrectly adding heavy taxes to poor citizen...
POSTCONDITION: Argument is 10, result is €12.20, condition is false
Exception: Result of adding tax to amount 10 has incorrect format: €12.20
-----------------------------------------``````

This output shows that, when the result of applying taxes to our poor citizen starts with the `\$` sign, everything works as expected, while, when it starts with the `€` sign, an `InvalidTaxResultFormatException` is thrown.

The `Postcondition` combinator comes in very handy when writing tests, though it might also be used for testing conditions that must hold true after executing some logic.

## Conclusion

This was just a brief and very informal introduction to combinators. In fully functional programming languages, they are extremely important to control the execution flow of applications. Although as of today they're not essential in Java, it's good to know about them, as the trend is to go more functional over time.

All the code and examples shown here are available in its own GitHub repo. Please contact me if you find something to correct or improve.

Scan Java, NuGet, and NPM packages for open source security and license compliance issues.

Topics:
java ,functional programming ,combinators ,java 8 ,tutorial

Comment (0)

Save
{{ articles[0].views | formatCount}} Views

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

### {{ parent.tldr }}

{{ parent.urlSource.name }}