DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Java
  4. How to Be More Functional in Java With Vavr

How to Be More Functional in Java With Vavr

Want to learn more about using vavr in Java to create more functional code? Check out this tutorial to learn more about using vavr.

Joaquin Caro user avatar by
Joaquin Caro
·
Aug. 14, 18 · Tutorial
Like (18)
Save
Tweet
Share
15.14K Views

Join the DZone community and get the full member experience.

Join For Free

With the release of Java 8, a new paradigm was discovered pertaining to development with Java, but one question arises — is it enough? And, what if we could have other functionalities of more purely functional languages in Java? To meet these needs, vavr was invented with the mission of reducing the code and making it more readable and robust with the immutability of the data. In this article, we will see how to be more functional in Java with vavr (if you are interested in knowing how to be more functional in PHP, check out this article.

How to Be More Functional in Java With Vavr

Vavr, among other things, includes immutability in lists and functions to work with them. It also includes some of the monads most used in other languages, with more functional, currying, and partial applications in functions.

Functions

Composition

With the arrival of Java 8, the class Function and BiFunction were included. We can define functions of one or two input parameters, for example:

Function<Integer, Integer> pow = (n) -> n * n;
assertThat(pow.apply(2)).isEqualTo(4);
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
assertThat(multiply.apply(10, 5)).isEqualTo(50);


With vavr, we can have functions up to eight parameters with the FunctionN  types

Function1<Integer, Integer> pow = (n) -> n * n;
assertThat(pow.apply(2)).isEqualTo(4);
Function3<Integer, Integer, Integer, Integer> multiply = (n1, n2, n3) -> n1 * n2 * n3;
assertThat(multiply.apply(5, 4, 3)).isEqualTo(60);


Besides being able to create functions of up to eight input parameters, it also offers us the composition of functions with operations  .andThen , .apply  , and .compose. 

Function1<String, String> toUpper = String::toUpperCase;
Function1<String, String> trim = String::trim;
Function1<String, String> cheers = (s) -> String.format("Hello %s", s);
assertThat(trim
            .andThen(toUpper)
            .andThen(cheers)
            .apply("   john")).isEqualTo("Hello JOHN");
Function1<String, String> composedCheer =
cheers.compose(trim).compose(toUpper);
assertThat(composedCheer.apply(" steve ")).isEqualTo("Hello STEVE");


Lifting

With lifting, we deal with exceptions when composing the functions, which the function will return an Option.none if an exception and Option.some have been executed correctly.
This is very useful when composing functions that use third-party libraries and can return exceptions.

Function1<String, String> toUpper = (s) -> {
    if (s.isEmpty()) throw new IllegalArgumentException("input can not be null");
    return s.toUpperCase();
};
Function1<String, String> trim = String::trim;
Function1<String, String> cheers = (s) -> String.format("Hello %s", s);
Function1<String, String> composedCheer = cheers.compose(trim).compose(toUpper);

Function1<String, Option> lifted = Function1.lift(composedCheer); 
assertThat(lifted.apply("")).isEqualTo(Option.none());
assertThat(lifted.apply(" steve ")).isEqualTo(Option.some("Hello STEVE"));

 

Partial Application

With the partial application, we can create a new function by setting n parameters to an existing one, where n will always be less than the arity of the original function. The return will also be an original arity function-parameters set:

Function2<String, String, String> cheers = (s1, s2) -> String.format("%s %s", s1, s2);
Function1<String, String> sayHello = cheers.apply("Hello");
Function1<String, String> sayHola = cheers.apply("Hola");
assertThat(sayHola.apply("Juan")).isEqualTo("Hola Juan");
assertThat(sayHello.apply("John")).isEqualTo("Hello John");


We have defined a generic cheers function that accepts two input parameters. We have derived this to two new sayHello  and sayHola, by applying it partially. We already have two more specific ones to say hello and we could derive more cases if we needed them.

Currying

Currying is the technique of decomposing a function of multiple arguments into a succession of functions of an argument.

Function3<Integer, Integer, Integer, Integer> sum = (a, b, c) -> a + b + c;
Function1<Integer, Function1<Integer, Integer>> add2 = sum.curried().apply(2);
Function1<Integer, Integer> add2And3 = add2.curried().apply(3);
assertThat(add2And3.apply(4)).isEqualTo(9);

 

Memoization

One of the premises of the functional programming is to have pure functions without side effects. This basically means that a function passing the same arguments always has to return the same result.
Therefore, if it always returns the same thing, why not cache it? This is the mission of memoization, caching the inputs and outputs of the functions to only launch them once.

void memoization() {
        Function1<Integer, Integer> calculate =
            Function1.of(this::aVeryExpensiveMethod).memoized();
        StopWatch watch = new StopWatch();
        watch.start();
        calculate.apply(40);
        System.out.println(watch.getTime());

        calculate.apply(40);
        System.out.println(watch.getTime());

        calculate.apply(50);
        System.out.println(watch.getTime());
}
private Integer aVeryExpensiveMethod(Integer number) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return number * number;
}

 

Monads

Try

The monad Try  includes an execution capturing of a possible exception. Its two possible return values are the case of failure by exception or the value if it has gone well.

Some useful methods of the Try  are:

 .isSuccess (): as the name itself indicates, returns a boolean by checking if it is a success

 .isFailure (): returns a boolean by checking if it is a failure

 get (): get the value in case it has gone correctly. If a get is made and it is not checked to see if it was done without being a success, it will drop the exception.

 map (): map over the value in case it went well. If it is a failure, it will not be executed. 

 getOrElse (T): this allows for returning a default value in the case of error. 

 getOrElse (Supplier): This allows the passing of another function in the case of error.

 recover (throwable -> {}): Same as getOrElse, but in this case, we will have the exception that has been thrown to be able to achieve it or to be able to return different values, depending on the type of exception. 


Function2<Integer, Integer, Integer> divide = (n1, n2) -> n1 / n2;
assertThat(Try.of(() -> divide.apply(10, 0)).isFailure()).isTrue();
assertThat(Try.of(() -> divide.apply(10, 5)).isSuccess()).isTrue();
assertThat(Try.of(() -> divide.apply(10, 5)).get()).isEqualTo(2);
assertThat(Try.of(() -> divide.apply(10, 0)).getOrElse(0)).isEqualTo(0);

 

Lazy

Lazy is a monad about a supplier to whom memoization is applied the first time it is evaluated.

Lazy<List> lazyOperation = Lazy.of(this::getAllActiveUsers);
assertThat(lazyOperation.isEvaluated()).isFalse();
assertThat(lazyOperation.get()).isNotEmpty();
assertThat(lazyOperation.isEvaluated()).isTrue();


Either

Either represents a value of two types — left and right by convention. Either will put the value in the Right when it is correct and in the Left when it is not. The result will always be a left or a right — it can never be both.

Data structures

Immutable lists

If one of the principles of functional programming is immutability, what happens when we define a list and add items? Well, we are mutating it. Vavr provides a specialization of List, which, once created, cannot be modified. Any operation of adding, deleting, or replacing, will give us a new instance with the changes applied.

import io.vavr.collection.List;
...
//Append
List original = List.of(1,2,3);
List newList = original.append(4);
assertThat(original.size()).isEqualTo(3);
assertThat(newList.size()).isEqualTo(4);
//Remove
List original = List.of(1, 2, 3);
List newList = original.remove(3);
assertThat(original.size()).isEqualTo(3);
assertThat(newList.size()).isEqualTo(2);
//Replace
List original = List.of(1, 2, 4);
List newList = original.replace(4,3);
assertThat(original).contains(1,2,4);
assertThat(newList).contains(1,2,3);


Besides the immutability, it also provides direct methods to operate the list without going through the stream, get the minimum, maximum, average value, etc. For more information about what this list can offer, check Javadoc. These are the main features that Vavr offers us; however, there are some more that help us to be more functional in a language, like Java, with vavr. 

Java (programming language)

Published at DZone with permission of Joaquin Caro. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Generate Code Coverage Report Using JaCoCo-Maven Plugin
  • Promises, Thenables, and Lazy-Evaluation: What, Why, How
  • Core Machine Learning Metrics
  • Using QuestDB to Collect Infrastructure Metrics

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: