Functional UIs with Java 8 and Vaadin JavaOne2014

Preview:

DESCRIPTION

Slides for the JavaOne 2014 talk about how the functional features of Java 8 and Vaadin empower you to write clearer UI code.

Citation preview

Functional Vaadin

Henri Muurimaa, SVP of Engineering henri@vaadin.com @henrimuurimaa

7

MissionMissionWhy do we exist

Make building amazing web applications easy

Build UIs with components

Vaadin X

Developer

Productivity

Rich

UX

Web application layers

JavaScriptWeb serverBackend Communication

JS required required required requiredVaadin

required optionalrequired optional

Web application layers

JavaScriptWeb serverBackend Communication

JS required required required required

Vaadin

required optionalrequired optional

1 layer vs

3 layers

Less code Less bugs Faster time-to-market

But can it scale?

Test results

20,622 AJAX requests / minute before exceeding 1% rejected connections

MPAA reports 1,4 billion movie tickets sold in 2009. !

~2,700 tickets / minute.

5,496 tickets / minute

~11,000 concurrent users

On a single Amazon EC2 Large instance

www.vaadin.com/blog/-/blogs/vaadin-scalability-study-quicktickets

Wrong, but the community is very active there

> 100.000 developers from > 10.000 cities > 450 add-ons in the marketplace

Other 4 %Asia

20 %

Americas 22 %

Europe 54 %

Open Source community

Apache-licensed

Demo time

github.com/hezamu/WorkoutTracker

What is Functional Programming?

A style of programming that expresses computation as the evaluation of mathematical functions

Recursion

Lazy evaluation

Lambda expressionsType theory

MonadsReferential transparency

Currying

Entscheidungsproblem

Pattern matching

Tuples

Something practical?

Side effects?

State?

Denied

Denied

Okay…

What’s in it for me?

A new way of thinking

A new way of programming

Write tight, robust and scalable code

What’s hot in Java 8

Improved Date API

New Java 8 Date API in action

public int monthAge() { return (new Date().getYear() - date.getYear()) * 12 + (new Date().getMonth() - date.getMonth()); }

// Java 8 version with the new Date API public int monthAge() { return (int) Period.between(date, LocalDate.now()).toTotalMonths(); }

Lambda expressions

Anonymous functions

Runnable r = () -> System.out.println("hello lambda!”);

Comparator<Integer> cmp1 = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);

// Anonymous onsite functions button.addClickListener(event -> System.out.println("Button clicked!"));

Comparator<Integer> cmp2 = (x, y) -> { return (x < y) ? -1 : ((x > y) ? 1 : 0); // Need return if not one liner };

Workout Tracker example

editor.clear.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { editor.clearValues(); updateRating(); } });

// Java 8 version with a lambda editor.clear.addClickListener(event -> { editor.clearValues(); updateRating(); });

Method references with the :: notation

! private void eventHandler(Button.ClickEvent event) { // do something about the button click }

button.addClickListener(this::eventHandler);

// If the handler method is static button.addClickListener(MyClass::eventHandler);

Workout Tracker example

!editor.activity.addValueChangeListener(new Property.ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { updateRating(); } });

editor.date.addValueChangeListener(this::updateRating);

Streams

Composable to new Streams with higher order functions

Stream != Collection

As lazy as possible

Create from a Collection or an Iterable

Input validation

private boolean areInputsValid() { Component component = null; for (Iterator<Component> iter = editor.iterator(); iter.hasNext(); iter.next()) { if (fieldNotValidating(component)) return false; } return true; }

// Java 8 version with anyMatch and a method reference private boolean areInputsValid() { return !StreamSupport.stream(editor.spliterator(), true) .anyMatch(this::fieldNotValidating); }

From rating to stars

StringBuilder stars = new StringBuilder("New Workout: "); for (int i = 0; i < rating; ++i) { stars.append(FontAwesome.STAR.getHtml()); } editor.title.setValue("New Workout: “ + stars);

String stars = IntStream.range(0, rating) .mapToObj(r -> FontAwesome.STAR.getHtml()) .collect(Collectors.joining("")); editor.title.setValue("New Workout: " + stars);

Higher order functions

A function that takes one or more functions as input

Returns a new stream by applying the given function to all elements of this stream. !

MapReturns a new stream consisting of the elements of this stream that match the given predicate.

Filter

SQL analogue: SELECT SQL analogue: WHERE

Workout Tracker Example

!

!

!

private List<Workout> findByAge(int maxMonths) { List<Workout> result = new ArrayList<>(); for (Workout w : workouts) { if (w.monthAge() < maxMonths) { result.add(w); } } Collections.sort(result, new Comparator<Workout>() { @Override public int compare(Workout o1, Workout o2) { return o2.monthAge() - o1.monthAge(); } }); ! return result; }

!

!

!

private Stream<Workout> findByAge(int maxMonths) { return workouts.stream() .filter(w -> w.monthAge() < maxMonths) .sorted(Comparator.comparing(Workout::monthAge) .reversed()); }

What is Functional Reactive Programming?

Observable

Consider a spreadsheet

RxJava

Quick peek at RxJava

Observable<String> letters = Observable.from(new String[] { "a", "b", "c" });

letters.subscribe(letter -> System.out.println("Got letter: " + letter));

Power of Observables - composition

Observable<String> letters = Observable.from(new String[] { "a", “b", "c" }); Observable<Integer> numbers = Observable.from(new Integer[] { 1, 2, 3 });

Observable<String> pairs = Observable.combineLatest(letters, numbers, (l, n) -> { return "" + l + " -> " + n; }); !pairs.subscribe(pair -> System.out.println("Got pair: " + pair));

RxVaadinwww.vaadin.com/blog/-/blogs/reactive-functional-ui-development-with-vaadin

Rating to stars, reactive style!

Observable<String> activities = RxVaadin.valuesWithDefault(editor.activity, null); Observable<String> durations = RxVaadin.valuesWithDefault(editor.duration, ""); Observable<Date> dates = RxVaadin.valuesWithDefault(editor.date, editor.date.getValue()); Observable<String> calories = RxVaadin.valuesWithDefault(editor.calories, ""); Observable<String> avgHRs = RxVaadin.valuesWithDefault(editor.avgHR, ""); Observable<String> maxHRs = RxVaadin.valuesWithDefault(editor.maxHR, ""); Observable<String> comments = RxVaadin.valuesWithDefault(editor.comment, "");

Composing the rating Observable

Observable<Integer> ratings = WorkoutRatingLogic.ratings(activities, durations, dates, calories, avgHRs, maxHRs, comments);

Observable<String> ratingStrings = ratings.map(rating -> { if (rating == null) { return "New Workout”; // No stars if required fields not ok } else { return IntStream.range(0, rating) .mapToObj(i -> FontAwesome.STAR.getHtml()) .collect(Collectors.joining("", "New Workout: ", "")); } });

Last step: update the UI

// Have the label update its value whenever the Observable // emits a value RxVaadin.follow(editor.title, ratingStrings);

// Disable or enable the add button based on if the rating // calculation was successful or not ratings.subscribe(rating -> editor.add.setEnabled(rating != null));

Summary

The functional additions in Java 8 rock

Observables allow us avoid the “callback hell” with events

Vaadin works brilliantly with both

Thank You! !

github.com/hezamu @henrimuurimaa

henri@vaadin.com

!

Join us for a chat and drinks at the Vaadin Meetup

!

Hotel Serrano

Right now!