lambdas: myths and mistakes

71
Lambdas: Myths and Mistakes by Richard Warburton

Upload: richardwarburton

Post on 09-May-2015

1.121 views

Category:

Technology


3 download

DESCRIPTION

Java 8 will ship with a powerful new abstraction - Lambda Expressions (aka Closures) and a completely retooled set of Collections libraries. In addition interfaces have changed through the addition of default and static methods. The ongoing debate as to whether Java should include such language changes has resulted in many vocal opinions being espoused. Sadly few of these opinions have been backed up by practical experimentation and experience. * Are these opinions just myths? * What mistakes does a developer make? * Can a ‘blue collar’ Java Developer cope with functional programming? * Can we avoid these mistakes in future? In London we’ve been running a series of hackdays trying out Lambda Expressions as part of the Adopt-a-JSR program and have been recording and analysing the results. This gives us unprecedented insight into what will happen in future.

TRANSCRIPT

Page 1: Lambdas: Myths and Mistakes

Lambdas:Myths andMistakesby Richard Warburton

Page 2: Lambdas: Myths and Mistakes

Learning FP is so much more than justknowing what map/fold/filter/etc. do. It's

more like getting good at Portal :)

@copumpkin

Page 3: Lambdas: Myths and Mistakes

Who am I?Currently work at jClarity

Focus on performance diagnostics and

static analysis

Adopt a JSR (Date and Time + Lambdas)

Writing a book for O'Reilly Media on

Lambda Expressions in Java 8.

Page 4: Lambdas: Myths and Mistakes

Talk StructureBackground and Motivation

Introduction to Lambda Expressions

Beyond the Myths

Functional Thinking

Page 5: Lambdas: Myths and Mistakes

Backgroundand Motvation

Page 6: Lambdas: Myths and Mistakes

Lambda Expressions arecoming in Java 8!

Page 7: Lambdas: Myths and Mistakes

Lots of discussion/debate

Page 8: Lambdas: Myths and Mistakes

How can we help?Adopt-a-JSR

Page 9: Lambdas: Myths and Mistakes

Adopt-a-JSR?Community driven standards feedback

Hackdays

Serve on Expert Groups

Initiated by Martijn Verburg

Page 10: Lambdas: Myths and Mistakes

Some discussionunimportant

Concrete Examples focus discussion

Page 11: Lambdas: Myths and Mistakes

Introductionto Lambda

Expressions

Page 12: Lambdas: Myths and Mistakes

Overview of LambdasGoal: Better Libraries

Example: Data Parallelism

Approach: Allow Code as Data

Page 13: Lambdas: Myths and Mistakes

Action Listenerbutton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("button clicked"); }});

Page 14: Lambdas: Myths and Mistakes

Code as Databutton.addActionListener( ?);

Page 15: Lambdas: Myths and Mistakes

Need a parameterbutton.addActionListener(event

);

Page 16: Lambdas: Myths and Mistakes

Lambda Examplebutton.addActionListener(event -> System.out.println("button clicked"));

Page 17: Lambdas: Myths and Mistakes

No parametersRunnable helloWorld = () -> System.out.println("Hello World");

Page 18: Lambdas: Myths and Mistakes

Variable CaptureString name = getUserName();button.addActionListener(event -> System.out.println("hi " + name));

Page 19: Lambdas: Myths and Mistakes

Functional InterfacesEverything in Java has a type

Problem: Need types to represent

Functions

Solution: Use interfaces

Page 20: Lambdas: Myths and Mistakes

Functional Interfacespublic interface ActionListener extends EventListener { public void actionPerformed(ActionEvent event);}

Page 21: Lambdas: Myths and Mistakes

Functional InterfacesWant to support automated Data

Parallelism

Construct for building computation

pipelines

Iterator with inversion of control

Page 22: Lambdas: Myths and Mistakes

External Iterationint count = 0;for (Artist artist : artists) { if (artist.isFrom("London")) { count++; }}

Page 23: Lambdas: Myths and Mistakes

Internal Iterationartists.stream() .filter(artist -> artist.isFrom("London"));

Page 24: Lambdas: Myths and Mistakes

map

Page 25: Lambdas: Myths and Mistakes

mapList<String> collected = Stream.of("a", "b", "hello") .map(string -> string.toUpperCase()) .collect(toList());

assertEquals(asList("A", "B", "HELLO"),collected);

Page 26: Lambdas: Myths and Mistakes

reduce

Page 27: Lambdas: Myths and Mistakes

reduceint count = Stream.of(1, 2, 3) .reduce(0, (acc, x) -> acc + 1);

assertEquals(3, count);

Page 28: Lambdas: Myths and Mistakes

filter

Page 29: Lambdas: Myths and Mistakes

filterList<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1") .filter(value -> isDigit(value.charAt(0))) .collect(toList());

assertEquals(asList("1abc"), beginningWithNumbers);

Page 30: Lambdas: Myths and Mistakes

Putting it all togetherfor a given an album, find the nationality of every band

playing on that album

Page 31: Lambdas: Myths and Mistakes

Putting it all together (2)1. transform an album into its artists2. figure out which artists are bands3. find the nationalities of each band

Page 32: Lambdas: Myths and Mistakes

Putting it all together (3)List<String> origins = album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());

Page 33: Lambdas: Myths and Mistakes

Method Referencesstr -> str.length

String::length

x -> foo.bar(x)foo::bar

str -> new Name(str)Name::new

Page 34: Lambdas: Myths and Mistakes

Beyond theMyths

Page 35: Lambdas: Myths and Mistakes

Claim: Syntax is the mostimportant thing aboutLambda Expressions

Page 36: Lambdas: Myths and Mistakes

Yeah, I liked the # syntax proposal better,too. One less character to type! :)

Page 37: Lambdas: Myths and Mistakes

Have you considered 'default null'? It willsave a keyword

Page 38: Lambdas: Myths and Mistakes

How about a single punctuation mark,currently unused, as syntax sugar for "()-

>".

Page 39: Lambdas: Myths and Mistakes

(_, _) -> _ + _

This is starting to look like risque ASCII art:)

Page 40: Lambdas: Myths and Mistakes

Its a Myth!

Page 41: Lambdas: Myths and Mistakes

Claim: Syntax is irrelevant

Page 42: Lambdas: Myths and Mistakes

// Originally invalidStream.of(1, 2, 3) .forEach(x -> System.out.println(x));

// Required extra ;Stream.of(1, 2, 3) .forEach(x -> System.out.println(x););

Page 43: Lambdas: Myths and Mistakes

Difference betweenexpectations

Many language features stolen! adapted

Missing Features

Stronger Type System

Tuples

List construction syntax

Page 44: Lambdas: Myths and Mistakes

Framing EffectDifferent reactions depending on whether something is

presented as a loss or a gain.

Page 45: Lambdas: Myths and Mistakes

Recall our earlier exampleList<String> origins = album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());

Page 46: Lambdas: Myths and Mistakes

Eager vs Lazy (2) album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality())

Page 47: Lambdas: Myths and Mistakes

Eager vs Lazy (3) album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());

Page 48: Lambdas: Myths and Mistakes

Very little TDDOnly a couple of developers used TDD or

unit tests

Maybe a reflection on general TDD

Maybe unfamiliarity with testing

functional code

Page 49: Lambdas: Myths and Mistakes

How do I test this?list.stream() .map(x -> 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(0, x))) .collect(toList());

Page 50: Lambdas: Myths and Mistakes

Approach 1: Testsurrounding function

Don't test the lambda

Test the method its surrounded by

Works well for simple lambdas

Page 51: Lambdas: Myths and Mistakes

Approach 2: ExtractMethod

double complexFunction(double x) { return 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(0, x));}

list.stream() .map(this::complexFunction) .collect(toList());

Page 52: Lambdas: Myths and Mistakes

Mistake: debugginglist.stream() .filter(filteringFunction) .map(mappingFunction) .collect(toList());

List<Bar> bars = new ArrayList<>();for (Foo element : list) { if (filteringFunction(element) { Bar result = mappingFunction(element); bars.add(result); }}

Page 53: Lambdas: Myths and Mistakes

peeklist.stream() .filter(filteringFunction) .peek(e -> System.out.println("Filtered value: " + e)); .map(mappingFunction) .map(e -> e); .collect(toList());

Page 54: Lambdas: Myths and Mistakes

Compiler Error Messages

Page 55: Lambdas: Myths and Mistakes

ComparatorsComparator<String> comparator = comparing(String::length);

Comparator<String> comparator = comparing(str -> str.length);

Page 56: Lambdas: Myths and Mistakes

Compiler Errorjava: reference to comparing is ambiguous bothmethod<T>comparing(java.util.function.ToIntFunction< ? super T>)in java.util.Comparator and method<T,U>comparing(java.util.function.Function< ? super T,? extends U>)in java.util.Comparator match

Page 57: Lambdas: Myths and Mistakes

What happened?// Generic object variantpublic static <T, U extends Comparable< ? super U>> Comparator<T> comparing(Function< ? super T, ? extends U> keyExtractor)

// Specialised primitive variantpublic static <T> Comparator<T> comparing(ToIntFunction< ? super T> keyExtractor)

Page 58: Lambdas: Myths and Mistakes

SummarySyntax important, but not in the way

people think

New approaches for debugging and

testing

Take care of overloads and compiler

error messages

Page 59: Lambdas: Myths and Mistakes

FunctionalThinking

Page 60: Lambdas: Myths and Mistakes

FunctionalThinking?

Thinking in terms of the input to output relationship andnot a sequence of steps

Page 61: Lambdas: Myths and Mistakes

First code that peoplewrite

List<Integer> numbers = Arrays.asList(1, 2, 3);numbers.forEach(x -> { System.out.println(x);});

Page 62: Lambdas: Myths and Mistakes

Non-idiomatic ProposalsEg: capture non-final local variables

Page 63: Lambdas: Myths and Mistakes

Example ProblemCount the number of instances of each word in a

document.

Page 64: Lambdas: Myths and Mistakes

Example Problem

Page 65: Lambdas: Myths and Mistakes

Ideal Solutionreader.lines() .flatMap(s -> Stream.of(s.split(" "))) .collect(groupingBy(s -> s, counting()));

Page 66: Lambdas: Myths and Mistakes

Ideal Solution (then)reader.lines() .flatMap(s -> Stream.of(s.split(" "))) .collect(groupingBy(s -> s, reducing(s -> 1, Integer::sum)));

// Map entries for "dad"// [ "dad", "dad", "dad" ] => [1, 1, 1] => 3

Page 67: Lambdas: Myths and Mistakes

Bad Solution (Part 1)Map<string, list<string="">> initial = br.lines() .flatMap(s -> Arrays.stream(s.split(" "))) .collect(groupingBy(s -> s)); Map<map.entry<string, integer="">, Integer> freq1 = initial .entrySet().stream() .map(entry -> new AbstractMap.SimpleImmutableEntry<string, integer="">(entry.getKey(), entry.getValue().size())) .collect(Collectors.toMap(entry -> entry.getValue())); </string,></map.entry<string,></string,>

Page 68: Lambdas: Myths and Mistakes

Bad Solution (Part 2) Supplier<hashmap<string, integer="">> supplier = () -> newHashMap<string, integer="">(); BiConsumer<hashmap<string, integer="">, Map.Entry<string, integer="">> accum = (HashMap<string, integer=""> result, Map.Entry<string, integer="">entry) -> result.put(entry.getKey(), entry.getValue()); BiConsumer<hashmap<string, integer="">, HashMap<string, integer="">>merger = HashMap::putAll;

Map<string, integer=""> freq2 = initial.entrySet().stream() .map(entry -> new AbstractMap.SimpleImmutableEntry<string, integer="">(entry.getKey(), entry.getValue().size())) .collect(supplier, accum, merger); </string,></string,></string,></hashmap<string,></string,></string,

Page 69: Lambdas: Myths and Mistakes

SummaryFunctional Thinking the most important

thing to learn

Not Java specific at all

Requires Practise

Page 70: Lambdas: Myths and Mistakes

ConclusionsGone through a bunch of examples of

specific issues

‘Functional Thinking’: Not necessary to

start learning.

Try before you buy

Page 71: Lambdas: Myths and Mistakes

Questions?@RichardWarburto

http://insightfullogic.com

https://github.com/RichardWarburton/