functional vaadin talk at oscon 2014
DESCRIPTION
An introduction on how to utilize functional programming techniques in Java 8 and Scala with Vaadin code.TRANSCRIPT
MissionMissionWhy do we exist
Make building amazing web applications easy
Building blocks
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
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(); } });
// Java 8 version with a method reference editor.date.addValueChangeListener(this::updateRating);
Streams
Composable with higher order functions
Streams != collections
As lazy as possible
Can be infinite
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); }
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
!
!
!
// Java 8 version with stream operations private Stream<Workout> findByAge(int maxMonths) { return workouts.stream() .filter(w -> w.monthAge() < maxMonths) .sorted(Comparator.comparing(Workout::monthAge).reversed()); }
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; }
Scratching the surface of Scala syntax
class Cat(name: String) { initLitterBox() def meow(volume: Int = 5) = { println(s"$name meows " + (if (volume <= 5) "quietly" else "loudly")) volume <= 5 } }
Class body is the constructor
identifier: type notation
Functions with def keyword
Arguments can have default values
Return keyword optional
No semicolons needed
Burn the boilerplate - Workout.java
!
!
!
public void setDuration(int duration) { this.duration = duration; } ! public double getAvgHR() { return avgHR; } ! public void setAvgHR(double avgHR) { this.avgHR = avgHR; } ! public double getMaxHR() { return maxHR; } ! public void setMaxHR(double maxHR) { this.maxHR = maxHR; } ! public int getCalories() { return calories; } ! public void setCalories(int calories) { this.calories = calories; } ! public String getComment() { return comment; } ! public void setComment(String comment) { this.comment = comment; } }
public class Workout { private String activity; private Date date; private int duration, calories; private double avgHR, maxHR; private String comment; ! public Workout(String activity, Date date, int time, double avgHR, double maxHR, int kcal, String comment) { this.activity = activity; this.date = date; this.duration = time; this.avgHR = avgHR; this.maxHR = maxHR; this.calories = kcal; this.comment = comment; } ! public int monthAge() { return (int) Period.between(date, LocalDate.now()).toTotalMonths(); } ! public String getActivity() { return activity; } ! public void setActivity(String activity) { this.activity = activity; } ! public Date getDate() { return date; } ! public void setDate(Date date) { this.date = date; } ! public int getDuration() { return duration; }
Equivalent Workout.scala
!
!
!
case class Workout(activity: String, date: LocalDate, duration: Int, avgHR: Double, maxHR: Double, calories: Int, comment: String) { def monthAge = Period.between(date, LocalDate.now).toTotalMonths }
github.com/henrikerola/scaladin
An example
// Scaladin val layout = new VerticalLayout { margin = true ! add(Label("Hello, OSCON!"), alignment = Alignment.TopCenter) add(Button("Click me”, handleButtonClick)) }
// Java 7 VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); Label label = new Label(“Hello, OSCON!”); layout.addComponent(title); layout.setComponentAlignment(label, Alignment.TOP_CENTER); !Button button = new Button(“Click me", new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { handleButtonClick(); } }); layout.addComponent(button);
Input validation, Java 8 & Scala
// Scaladin version of the editor gives us the components as a Scala Set // which supports functional operations. private def areInputsValid = !editor.components.exists(fieldNotValidating)
// Java 8 version with anyMatch and a method reference private boolean areInputsValid() { return !StreamSupport.stream(editor.spliterator(), true) .anyMatch(this::fieldNotValidating); }
Summary
SLOC comparison
Java 7 Java 8 Scala
UI 267 264 175
Presenter 168 128 84
POJO 82 82 8All versions: zero lines of HTML, JavaScript, RPC code or browser specific tweaks
Je zult maar letter wezen. Goed, ik ben niet ontevredet. Maar het valt niet mee in deze zeventiger jaren tot het vaderlandse alfabet te behoren. Foto-zetterijen wringen je steeds in steeds ingevikkelder. Je zult maar letter wezen. Goed, ik ben
Henri Muurimaa, SVP of [email protected] +358 400 474778 @henrimuurimaa