lambdas myths-and-mistakes

70
Lambdas: Myths and Mistakes by Richard Warburton

Upload: richardwarburton

Post on 30-Jun-2015

692 views

Category:

Technology


0 download

DESCRIPTION

tl;dr - How will the everyday developer cope with Java 8’s Language changes? 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. Huge topics of mailing list discussion have been almost entirely irrelevant problems to developers, and some issues which barely got any coverage at all have proved to be a consistent thorn in people’s side.

TRANSCRIPT

Page 1: Lambdas myths-and-mistakes

Lambdas:Myths andMistakesby Richard Warburton

Page 2: Lambdas myths-and-mistakes

the critical design tool for softwaredevelopment is a mind well educated indesign principles. It is not ... technology.

Craig Larman

Page 3: Lambdas myths-and-mistakes

Who am I?Currently work at jClarity

Focus on Performance Tuning

Adopt a JSR (Date and Time + Lambdas)

Impending O'Reilly book on Lambda

Expressions in Java 8.

Page 4: Lambdas myths-and-mistakes

Talk StructureWhy am I talking about this?

Intro to Lambda Expressions

Beyond the Myths

Functional Thinking

Page 5: Lambdas myths-and-mistakes

Why am Italking about

this?

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?More community driven standards

Hackdays

Serve on Expert Groups

Page 10: Lambdas myths-and-mistakes

Some discussionunimportant

Concrete Examples focus discussion

Page 11: Lambdas myths-and-mistakes

Intro toLambda

Expressions

Page 12: Lambdas myths-and-mistakes

Overview of LambdasGoal: Better Libraries

Example: Collections with 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

StreamsSupport automated data parallelism

Build 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")) .count();

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 sum = Stream.of(1, 2, 3, 4) .reduce(0, (acc, x) -> acc + x);

assertEquals(10, sum);

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.lengthString::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())

// What's happened? .collect(toList());

Page 47: Lambdas myths-and-mistakes

Very little TestingMaybe ...

a reflection on popularity of TDD

spikes are good for learning

unfamiliarity with testing lambdas

Page 48: Lambdas myths-and-mistakes

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

Page 49: Lambdas myths-and-mistakes

Approach 1: Testsurrounding methodDon't test the lambda

Test the method its surrounded by

Works well for simple lambdas

Page 50: 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 51: Lambdas myths-and-mistakes

Mistake: debugging// Streamslist.stream() .filter(filteringFunction) .map(mappingFunction) .collect(toList());

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

Page 52: Lambdas myths-and-mistakes

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

Page 53: Lambdas myths-and-mistakes

Compiler Error Messages

Page 54: Lambdas myths-and-mistakes

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

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

Page 55: 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 56: 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 57: 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 58: Lambdas myths-and-mistakes

FunctionalThinking

Page 59: Lambdas myths-and-mistakes

FunctionalThinking?

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

Page 60: Lambdas myths-and-mistakes

First code that peoplewrite

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

Page 61: Lambdas myths-and-mistakes

Non-idiomatic ProposalsEg: capture non-final local variables

Page 62: Lambdas myths-and-mistakes

Example ProblemCount the number of instances of each word in a

document.

Page 63: Lambdas myths-and-mistakes

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

Page 64: 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 65: 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()));

Page 66: 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);

Page 67: Lambdas myths-and-mistakes

This takes thought

Page 68: Lambdas myths-and-mistakes

SummaryIdioms are vital

Not Java specific at all

Requires Practise

Page 69: 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 70: Lambdas myths-and-mistakes

Questions?@RichardWarburtohttp://jclarity.com

http://insightfullogic.com

https://github.com/RichardWarburton/