SlideShare ist ein Scribd-Unternehmen logo
1 von 39
Downloaden Sie, um offline zu lesen
FUNCTIONAL
PROGRAMMING & JAVA
Cenni di programmazione Funzionale
e Java Optional e Stream
PROGRAMMAZIONE
FUNZIONALE (FP)
FUNZIONALE?
3
[Wikipedia]
FP
• Un linguaggio può favorire uno stile di programmazione rispetto ad un altro, ma nulla di
più.
• Alcuni problemi sono più facilmente risolvibili con un approccio rispetto che con un altro.
• Le nuove implementazioni in Java8 rendono più semplice scrivere parti usando un
approccio funzionale, ma era possibile anche prima (con librerie esterne come Guava).
• Javascript consente un approccio event driven così come un approccio funzionale.
• Javascript è un linguaggio non tipizzato (loosely typed) e questo lo rende estremamente
flessibile, quindi anche l’approccio funzionale è del tutto possibile.Tuttavia è abbastanza
buffo se si considera che i linguaggi pensati per un approccio funzionale hanno solitamente
un forte controllo sui tipi per fornire un aiuto nella rilevazione di eventuali errori.
4
FP INTIME
• Si basa sul Lambda Calculus, sviluppato nel 1930
• LISP è stato il primo linguaggio funzionale (1960)
• Negli anni ’70 è stato sviluppato ML (MetaLanguage) presso l’università di Edimburgo
5
[https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-1-1f15e387e536#.muqfaya41]
IN MATEMATICA: FUNZIONI
• In matematica, una funzione è una relazione tra due insiemi, chiamati dominio e codominio della
funzione, che associa ad ogni elemento del dominio uno e un solo elemento del codominio.
• Se i due insiemi sono rispettivamente indicati con X eY, la relazione è indicata con f:X⟶Y e
l'elemento associato a x ∈ X tramite la funzione f viene abitualmente indicato con f(x) (si
pronuncia "effe di x”).
6
[Wikipedia]
COMPOSIZIONE DI FUNZIONI
In matematica, la composizione di funzioni è l'applicazione di una funzione al risultato di un'altra funzione.
Più precisamente, una funzione f tra due X eY trasforma ogni elemento di X in uno diY:
in presenza di un'altra funzione g che trasforma ogni elemento diY in un elemento di un altro insieme Z, si definisce la
composizione di f e g come la funzione che trasforma ogni elemento di X in uno diY usando prima f e poi g. Il
simbolo Unicode dell'operatore è ∘ (U+2218). (Wikipedia)
• Questo significa che si possono comporre solo funzioni il cui
codominio di una sia il dominio della successiva che verrà
applicata (quindi il controllo sui tipi aiuta!).
• Nei linguaggi di programmazione che consideriamo le
funzioni ritornano un solo valore (che ovviamente può
essere di qualsiasi tipo: numero, stringa, array, oggetto,
funzione…) Quindi ci troveremo a comporre funzioni con
un solo argomento come parametro.
• g∘f (x) = g(f(x)) quindi viene prima applicato f e poi g.
7
[Wikipedia]
FP:ALCUNI ASPETTI
• Le funzioni sono first class members
• Pure functions
• Immutabilità
• Function composition
• Currying
• Functor e Monad (either, optional, Java stream)
8
FIRST CLASS MEMBERS
• in un linguaggio funzionale, le funzioni sono un tipo di dati esattamente come gli altri (un numero, un testo). Quindi
possono essere passate come parametro ad un metodo o essere restituite come output di un altro.
• in Java8 questo è possibile, ma a volte è abbastanza prolisso (l’inferenza dei tipi ha ancora limiti, è necessario un uso esteso
dei generici, e c’è la necessità di dichiarare tipi se le interfacce funzionali già a disposizione non sono sufficienti)
• in JavaScript invece la cosa è del tutto normale
9
Unnecessary function wrapping:
in Javascript è totalmente
evitabile, in Java relativamente
alle possibilità offerte dal
linguaggio
[https://github.com/MostlyAdequate/mostly-adequate-guide]
PURE FUNCTIONS
“A pure function is a function that, given the same input, will always return the same
output and does not have any observable side effect. […]
A side effect is a change of system state or observable interaction with the outside
world that occurs during the calculation of a result.” (Mostly Adequate Guide to
Functional Programming)
Quindi una funzione che non dipende dallo stato di variabili esterne e che non altera i
dati (in JS ad esempio array.splice che modifica l’array vs array.slice che torna una
shallow copy modificata), non accede al file system o a un database…
Ovviamente, un software per poter funzionare, provocherà
inevitabilmente delle alterazioni all’esterno, l’obiettivo è relegare e
minimizzare le parti di codice impuro.
10
PURE FUNCTIONS WHY?
• Portable (self explanatory)
• Testable
• Referential transparency (un blocco di codice che può
essere sostituito con il suo valore valutato senza che
questo modifichi il comportamento del programma)
• Parallel code
11
IMMUTABILITÀ
• Nei linguaggi di programmazione puramente funzionali non esiste il concetto di
“variabile”, quando un valore viene assegnato ad un parametro x, questo rimane
immutabile per tutta la durata della vita di x.
• Quindi il concetto di loop non esiste in programmazione funzionale pura (su
cosa faccio il loop se non ho le variabili?!?).
• Ma, senza variabili? Si compongono funzioni. (in ogni caso nei linguaggi
funzionali si possono definire dei parametri per migliorare la leggibilità, solo che
una volta che gli è stato assegnato il valore, non è più possibile mutarlo)
• Ma, senza i loop? Dipende dal caso, o con la ricorsione o sfruttando le
funzionalità offerte in altro modo sulle collezioni di item.
12
CURRYING
13
Dicevamo che la composizione di funzioni si può fare solo su funzioni di un solo
parametro. E per funzioni che richiedono più parametri?
“In mathematics and computer science, currying is the technique of translating
the evaluation of a function that takes multiple arguments (or a tuple of arguments)
into evaluating a sequence of functions, each with a single argument.
Currying provides a way for working with functions that take
multiple arguments, and using them in frameworks where functions might
take only one argument” (Wikipedia)
Questo avviene in Javascript solitamente attraverso delle librerie aggiuntive.
Chiamando una funzione con meno argomenti di quelli che richiede, si ottiene una
nuova funzione che richiede gli elementi rimanenti.
Si chiama così in onore di Haskell Curry.
FUNCTOR
• Esiste una branca della matematica: Category theory, che cerca di unificare altre branche (l’idea di base è che
una categoria consiste in oggetti e funzioni). Chi ha sviluppato i primi linguaggi di programmazione funzionali si
è ispirato a concetti dalla teoria delle categorie. Un concetto interessante è quello del Functor.
• Per quanto ci riguarda l’idea è quella di avere un contenitore, in grado di contenere una proprietà. Quando la
proprietà viene messa nel contenitore, ci resta. Se il nostro contenitore si chiama C, il suo metodo of() crea un
contenitore C con all’interno il valore passato. Possiamo tirarla fuori, ma ha senso farlo solo in particolari
momenti. La sua utilità sta nel poter operare su C senza sapere necessariamente cosa c’è dentro.
• Una volta che la proprietà è nel contenitore, vogliamo applicare su di essa delle funzioni: ecco il metodo map().
• A Functor is a type that implements map and obey some laws
• E tanto basta, altrimenti ci si può avventurare nella CategoryTheory (per alcuni link vedi più avanti)
14
[https://github.com/MostlyAdequate/mostly-adequate-guide]
MONAD
• Allo stesso modo si può sentir parlare di monadi, per il momento ci
basta tenere presente questa definizione a grandi linee:
• “Any functor which defines a join method, has an of method, and
obeys a few laws is a monad. ”
• Il metodo join si occupa di appiattire: dato che invocare map()
oppure of() più volte può generare una proliferazione di
contenitori che contengono contenitori che contengono
contenitori… serve un modo per appiattire il tutto: in pseudo
codice l’idea è C(C(C(x))).join() = C(C(x))
15
FP IN JAVASCRIPT
• Abbiamo detto che esistono librerie che rendono possibile e semplice la programmazione funzionale in JS. Fantasyland
specs mira a fornire le specifiche che tali librerie devono implementare (per i più avventurosi c’è il link più avanti).
• JS fornisce nativamente alcune funzionalità, tramite funzioni già implementate. Si pensi a Array.map():
16
• Per altre funzionalità si possono utilizzare librerie come lodash-fp o ramdajs. Tipicamente:
• Null-check problem: la monade Maybe può essere usata, dato consente di concatenare vari metodi map in
modo da applicare la funzione solo se il contenuto di Maybe è un valore, altrimenti viene tornata la
Monade Maybe.of(null)
• La gestione delle eccezioni con la possibilità di terminare subito dopo un errore: questo può essere gestito
tramite la monade Either, che ha due costruttori: Either.Left e Either.Right. L’idea è quella di memorizzare
l’errore in Left e i valori utili in Right. Sia Left che Right implementano map ma in modo da propagare e
conservare l’errore (un po’ come null nella monade Maybe)
FP IN DELPHI
• In Delphi data la facilità con cui si possono definire nuovi tipi, è molto semplice dichiarare che un metodo
(function o procedure) accetta come parametro un altro metodo.
• In ogni caso, il pas System.SysUtils contiene un insieme di dichiarazioni utili di tipi di metodi.
• Con il Delphi 2010 sono stati introdotti i metodi anonimi, sono metodi che possono essere scritti inline
per essere passati come parametro in un altro metodo, rimangono prolissi, ma possono risultare utili
17
FP GOOD READINGS
• Articoli introduttivi: https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-
part-1-1f15e387e536#.rqaf0fx53 e https://medium.freecodecamp.org/functional-programming-in-js-with-
practical-examples-part-1-87c2b0dbc276
• Fantasyland spec: https://github.com/fantasyland/fantasy-land
• Professor Frisby’s “Mostly adequate guide to Functional Programming” (https://github.com/
MostlyAdequate/mostly-adequate-guide) divertente e accurato, analizza JavaScript, ma i concetti sono
interamente applicabili aTypeScript e a Java e Delphi in una certa misura
• You don’t know JavaScript (https://github.com/getify/You-Dont-Know-JS) Riguarda solo JS, solo per chi
ama sapere perché le cose funzionano come funzionano.
• Category Theory https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-
preface/, o in alternativa la sua serie di video https://www.youtube.com/watch?v=I8LbkfSSR58
• Video sulla programmazione funzionale in Delphi https://www.youtube.com/watch?v=HDhmUjzUNyQ
18
STREAM E OPTIONAL
UN PASSO INDIETRO
• Gli Stream, java.util.function, Optional, lambda
expression, method reference…sono un insieme
di strumenti
• rendono possibile un approccio funzionale a
(certi) problemi
20
AGGIUNTE:A COLLECTION
21
1 package java.util;
2
3 public interface Collection<E> extends Iterable<E> {
4 // ...
24 default boolean removeIf(Predicate<? super E> filter) {
25 Objects.requireNonNull(filter);
26 boolean removed = false;
27 final Iterator<E> each = iterator();
28 while (each.hasNext()) {
29 if (filter.test(each.next())) {
30 each.remove();
31 removed = true;
32 }
33 }
34 return removed;
35 }
36 }
396 private static void validatePendingConfigurations() {
397 final Set<String> toBeRemoved = Sets.newHashSet();
398 final ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
399
400 for (final String configuration : pendingConfigurations) {
401 if (null == classLoader.getResourceAsStream(configuration)) {
402 log.info("Removing a configuration: " + configuration);
403 toBeRemoved.add(configuration);
405 }
406 }
407
408 for (final String configuration : toBeRemoved) {
409 pendingConfigurations.remove(configuration);
410 }
411 }
396 private static void validatePendingConfigurations() {
397 final ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
398 pendingConfigurations.removeIf(c ->
399 null == classLoader.getResourceAsStream(configuration));
400 }
AGGIUNTE:A ITERATOR
• In alcuni casi ho a disposizione solo un
Iterator, quindi non posso usare
Collection.forEach
• Ad esempio
AbstractIndexedLayout.getComponents()
• In questi casi si può usare
Iterator.forEachRemaining
22
1 package java.util;
2
3 import java.util.function.Consumer;
4
5 /**
6 * ...
7 */
8 public interface Iterator<E> {
9
10
11 /**
12 * Performs the given action for each remaining element until all
13 elements
14 * have been processed or the action throws an exception. Actions are
15 * performed in the order of iteration, if that order is specified.
16 * Exceptions thrown by the action are relayed to the caller.
17 *
18 * @implSpec
19 * <p>The default implementation behaves as if:
20 * <pre>{@code
21 * while (hasNext())
22 * action.accept(next());
23 * }</pre>
24 *
25 * @param action The action to be performed for each element
26 * @throws NullPointerException if the specified action is null
27 * @since 1.8
28 */
29 default void forEachRemaining(Consumer<? super E> action) {
30 Objects.requireNonNull(action);
31 while (hasNext())
32 action.accept(next());
33 }
34 }
• Vedi la presentazione su Java8
• Tutto il package java.util.function contiene le interfacce e metodi estremamente utili, sia
nell’uso all’interno degli Stream che per un approccio funzionale. Va esplorato
(Function.compose(), Predicate.and()…)
• Voglio trasformare una lista di oggetti X in una mappa con in chiave gli stessi oggetti? ho
il metodo UnaryOperatoridentity
• Voglio filtrare solo gli oggetti nulli o solo i non nulli? ecco ObjectsnonNull e
Objectsnull
• Voglio filtrare una collezione di oggetti recuperando solo quelli di classe AClass, e
ritornare una List<AClass>? (E non è un’aggiunta da Java 8, ma qui tornano
comodi…) Esistono AClassisInstance e AClasscast
INSOMMA:AGGIUNTE!
OPTIONAL
• Rappresenta più o meno la monade Maybe:
• è un contenitore che può contenere un valore, o null. Ha quindi i metodi per
ottenere un Optional da un valore o da null
• ha il metodo map che trasforma il suo contenuto se è diverso da null, altrimenti
torna Optional.ofNull
• è generico rispetto a cosa contiene: Optional<Integer> o = Optional.of(3)
• è stato introdotto principalmente per lavorare con gli Stream
• ha altri metodi utili come ad esempio filter: ritorna l’optional stesso se il valore
all’interno verifica il predicato, altrimenti ritorna l’optional di null.
24
JAVA STREAM
L’astrazione principale introdotta è lo stream, (in generale uno stream è la
coppia elemento n-simo e la modalità con cui generare il successivo). Sono
generici nel tipo del loro contenuto.
Gli stream sono diversi dalle collezioni per vari motivi:
• è funzionale, (nel senso di FP) quindi operazioni sugli stream producono
un risultato, ma non vanno a modificare la sorgente dello stream
• è intrinsecamente lazy, le operazioni sugli stream sono divise in due tipi:
intermedie (filter, map…) e terminali (collect, sum…). Le operazioni
intermedie sono sempre lazy.
• uno stream non ha necessariamente una fine
• Gli elementi di uno stream sono visitati una sola volta durante la vita
di uno stream, per visitarli di nuovo, va creato un nuovo stream
• Ci sono vari metodi per ottenere uno stream, quello tipico è
Collection.stream(), e anche Arrays.stream(). Ci sono inoltre i metodi statici
delle varie classi Stream o IntStream…
• Le operazioni sugli stream sono divise in intermedie e terminali e combinate
assieme in uno stream pipeline che è quindi sempre composto da:
• uno stream sorgente,
• 0 o più operazioni intermedie ed
• una operazione terminale.
• Le operazioni intermedie sono quelle che ritornano un nuovo stream.
Sono sempre lazy nel senso che quando ad esempio si esegue un
operazione di filtro con il metodo filter() questa non esegue il filtro subito,
ma crea un nuovo stream che, una volta attraversato, contiene solo gli
elementi dello stream originale che verificano la condizione del filtro.
• Le operazioni terminali possono visitare tutto o parte dello stream e/
o produrre un side effect. Dopo che l’operazione terminale è stata eseguita
lo stream è consumato e non più riutilizzabile (nella quasi totalità dei casi).
PER ESEMPIO (1)
[http://files.zeroturnaround.com/pdf/zt_java8_streams_cheat_sheet.pdf]
Operazioni intermedie
• filter: ritorna uno stream con i soli elementi che soddisfano il
predicato passato
• map: trasforma lo stream in un nuovo stream contenente il
risultato della funzione passata (applicata sugli elementi dello
stream originale)
• distinct: ritorna uno stream di elementi unici nello stream
• limit: ritorna uno stream che contiene solo il numero di
elementi passati
Operazioni terminali
• reduce: serve ad accumulare sugli elementi dello stream
• collect: ritorna una collezione (List, Set…) composta dagli
elementi dello stream
• forEach: per eseguire operazioni che hanno un effetto
secondario sugli elementi
27
• Le operazioni intermedie si possono dividere in due sottotipi: stateless e stateful (come
distinct e sorted). Le operazioni di tipo stateful hanno bisogno dello stato dell’elemento
precedente dello stream, inoltre possono aver bisogno di processare l’intero stream prima di
produrre un risultato.
• Gli stream facilitano l’esecuzione parallela del codice, ma in questa sede non approfondiamo,
l’unica cosa che vale la pena ricordare: side-effects prodotti dentro ad uno stream possono portare
a problemi di thread-safety. Quindi cercare di evitare il più possibile cose del genere:
[Stream Javadoc]
[Stream Javadoc]
INTERMEDIE
• Abbiamo detto che le operazioni intermedie sono lazy
nel senso che tutte producono un nuovo stream.
• Proprio per questo l’ordine in cui concateno le
operazioni intermedie è estremamente importante:
• se possibile, le operazioni di filter vanno messe per
prime, dato che fanno diminuire il numero di elementi
di input che proseguono lungo la pipeline
29
INTERMEDIE: FLATMAP
• FlatMap serve nel caso in cui si stia manipolando un insieme di
oggetti che a loro volta contengono delle collezioni da
manipolare, l’idea è che da ogni elemento dello stream originale si
produca un una serie di stream che verranno accodati assieme.
30
TERMINALI: COLLECT
• Collect trasforma gli elementi
dello Stream in un tipo diverso
di risultato.
• Java 8 fornisce la classe
Collectors (toSet(), toMap()…)
• è comunque possibile
implementare un proprio
collector
31
[http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/]
TERMINALI: REDUCE
Reduce combina tutti gli elementi di uno stream
in un unico risultato. Sono supportati tre tipi di
reduce.
• Optional<T> reduce(Bifunction<T> accumulator)
• T reduce(T identity, BinaryOperator<T>
accumulator)
32
[http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/]
• <U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
La terza firma di reduce si utilizza (e
il combiner viene effettivamente
invocato) se si eseguono gli stream
in parallelo. L’idea è che il combiner
viene eseguito quando arriva il
momento di rimettere assieme le
varie parti eseguite in parallelo.
UTILI
• IntStream.range(startIncluded, endExcluded) e IntStream.of al posto di un ciclo for.
• Esistono gli stream di primitive: IntStream, LongStream e DoubleStream. Essi supportano particolari funzioni
terminali come sum() e average(). Inoltre ciascuno ha il metodo boxed() per passare allo Stream
corrispondente: IntStream.boxed() produce uno Stream<Integer>. Il contrario può essere fatto con i
metodi mapToInt(), mapToLong() e mapToDouble().
• Da array a stream e viceversa: Arrays.stream() e Stream.toArray()
• La concatenazione di stream: Stream.concat() o Stream.of()
• Non è possibile riutilizzare uno stesso Stream, ma si può usare Supplier:
• A volte non serve usare gli stream, se si vuole semplicemente iterare su una Collection si può utilizzare
forEach()
• Collectors (toList, toMap, groupingBy e un Collector ad hoc)
33
[http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/]
FP O IMPERATIVE?
Di base va tenuto presente un unico concetto: è poco leggibile (a volte pericoloso) e poco sensato mescolare
programmazione imperativa e funzionale. Se si sta scrivendo un algoritmo usando uno stile, si deve cercare di evitare
di usare simultaneamente anche l’altro (quanto meno chiedersi e investigare se si può fare meglio)
• Le IDE danno ottimi suggerimenti sul come migliorare il codice, analizziamo i warning che danno:
• Ad esempio: usiamo i reference method, non stream.map(placeplace.getName()) ma stream.map(PlacegetName)
• Non stream().xxx.collect(toList()).toArray(new String[]{}) ma stream().xxx.toArray(String[]new)
Terminale
Stream sorgente
PER ESEMPIO (2)
Diventa, evitando di passare da funzionale a imperativo:
Torna un Optional<Filter>
Torna un Optional<Boolean>
PER ESEMPIO (3)
161 protected boolean doDraw() {
162 placesTableLayout.removeAllComponents();
163 Set<LocalGeodesy> currentSource = getCurrentSourceValues();
164 if (!currentSource.isEmpty()) {
165 double totalLength = currentSource.stream()
166 .filter(geodesy -> geodesy instanceof LocalPath)
167 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum();
168 if (totalLength != 0.0d) {
169 double controlledLength = currentSource.stream()
170 .filter(geodesy -> geodesy instanceof LocalPath
171 && PlannerMapFilterLayout.CONTROLLED.equals(geodesy.getColor()))
172 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum();
173 double needControlLength = currentSource.stream()
174 .filter(geodesy -> geodesy instanceof LocalPath
175 && NEED_CONTROL.equals(geodesy.getColor()))
176 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum();
177 double expiredLength = totalLength - controlledLength - needControlLength;
178 long numOfControlledPlaces = currentSource.stream()
179 .filter(geodesy -> CONTROLLED.equals(geodesy.getColor()))
180 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count();
181 long numOfNeedControlPlaces = currentSource.stream()
182 .filter(geodesy -> NEED_CONTROL.equals(geodesy.getColor()))
183 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count();
184 long numOfExpiredPlaces = currentSource.stream()
185 .filter(geodesy -> EXPIRED.equals(geodesy.getColor()))
186 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count();
187 Chart chart = getMetersControlledPie(numOfControlledPlaces, controlledLength,
188 numOfNeedControlPlaces, needControlLength, numOfExpiredPlaces,
189 expiredLength);
190 chart.setHeight("150px");
191 placesTableLayout.addComponent(chart);
192 }
193 updateNeedControlPlacesTable(currentSource);
194 placesTableLayout.addComponent(needControlTable);
195 placesTableLayout.addComponent(involvedPlacesTable);
196 placesTableLayout.setExpandRatio(involvedPlacesTable, 1.0f);
197 placesTableLayout.setExpandRatio(needControlTable, 1.0f);
198 }
199 return super.doDraw();
200 }
…DIVENTA
83 protected static double getLengthOf(Predicate<LocalGeodesy> predicate, Set<LocalGeodesy> geodesies) {
84 if (predicate == null) {
85 predicate = g -> true;
86 }
87 return geodesies.stream()
88 .filter(predicate.and(LocalPath.class::isInstance))
89 .map(LocalPath.class::cast)
90 .mapToDouble(LocalPath::getLength).sum();
91 }
92
93 protected static long countIf(Predicate<LocalGeodesy> predicate, Set<LocalGeodesy> geodesies) {
94 if (predicate == null) {
95 predicate = g -> true;
96 }
97 return geodesies.stream()
98 .filter(predicate)
99 .mapToLong(geodesy -> geodesy.getPlace().getId())
100 .distinct().count();
101 }
127 static final Predicate<LocalGeodesy> isExpired =
geodesy -> EXPIRED.equals(geodesy.getColor());
128 static final Predicate<LocalGeodesy> isControlled =
geodesy -> CONTROLLED.equals(geodesy.getColor());
129 static final Predicate<LocalGeodesy> needsControl =
geodesy -> NEED_CONTROL.equals(geodesy.getColor());
153 @Override
154 protected boolean doDraw() {
155 placesTableLayout.removeAllComponents();
156 Set<LocalGeodesy> currentSource = getCurrentSourceValues();
157 if (!currentSource.isEmpty()) {
158
159 double totalLength = getLengthOf(null, currentSource);
160 if (totalLength != 0.0d) {
161 double controlledLength = getLengthOf(isControlled, currentSource);
162 double needControlLength = getLengthOf(needsControl, currentSource);
163
164 double expiredLength = totalLength - controlledLength - needControlLength;
165
166 long numOfControlledPlaces = countIf(isControlled, currentSource);
167 long numOfNeedControlPlaces = countIf(needsControl, currentSource);
168 long numOfExpiredPlaces = countIf(isExpired, currentSource);
169
170 Chart chart = getPieChart(numOfControlledPlaces, controlledLength,
171 numOfNeedControlPlaces, needControlLength, numOfExpiredPlaces,
172 expiredLength, anyMatch(LocalCoordinates.class::isInstance,
currentSource.stream()));
173 chart.setHeight("250px");
174 placesTableLayout.addComponent(chart);
175 }
176 updateNeedControlPlacesTable(currentSource);
177 placesTableLayout.addComponent(needControlTable);
178 placesTableLayout.addComponent(involvedPlacesTable);
179 placesTableLayout.setExpandRatio(involvedPlacesTable, 1.0f);
180 placesTableLayout.setExpandRatio(needControlTable, 1.0f);
181 }
182 return super.doDraw();
183 }
JAVA 8 E FP: LIMITAZIONI
• Non esiste un’implementazione di Either
• Optional è stata introdotta solo dopo molte discussioni e solo per il suo
uso per gli Stream, viene consigliato di non usarla al di fuori di quel contesto
• Le varie interfacce in java.util.function non appartengono ad una stessa
gerarchia comune, quindi non si riescono a comporre (in senso matematico)
dato che solo Function ha i metodi compose e andThen
• Tuttavia le lambda possono diventare qualunque cosa (nei limiti del loro
tipo), basta forzare la type inference: .filter(((Predicate<ThingMO>)
this::isInlet).negate())
38
JAVA GOOD READINGS
• http://www.studytrails.com/java/java8/
Java8_Lambdas_FunctionalProgramming.jsp
• https://zeroturnaround.com/rebellabs/java-8-streams-cheat-sheet/
• https://dzone.com/articles/introduction-writing-custom
• http://www.nurkiewicz.com/2014/07/introduction-to-writing-
custom.html
• http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
39

Weitere ähnliche Inhalte

Was ist angesagt?

Lezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in JavaLezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in JavaAndrea Della Corte
 
Git basics to advance with diagrams
Git basics to advance with diagramsGit basics to advance with diagrams
Git basics to advance with diagramsDilum Navanjana
 
Spring boot
Spring bootSpring boot
Spring bootsdeeg
 
Java Spring framework, Dependency Injection, DI, IoC, Inversion of Control
Java Spring framework, Dependency Injection, DI, IoC, Inversion of ControlJava Spring framework, Dependency Injection, DI, IoC, Inversion of Control
Java Spring framework, Dependency Injection, DI, IoC, Inversion of ControlArjun Thakur
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기Arawn Park
 
Introduction to Spring WebFlux #jsug #sf_a1
Introduction to Spring WebFlux #jsug #sf_a1Introduction to Spring WebFlux #jsug #sf_a1
Introduction to Spring WebFlux #jsug #sf_a1Toshiaki Maki
 
SPRING - MAVEN - REST API (ITA - Luglio 2017)
SPRING - MAVEN - REST API (ITA - Luglio 2017)SPRING - MAVEN - REST API (ITA - Luglio 2017)
SPRING - MAVEN - REST API (ITA - Luglio 2017)Valerio Radice
 
Adobe AEM core components
Adobe AEM core componentsAdobe AEM core components
Adobe AEM core componentsLokesh BS
 
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorReactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorVMware Tanzu
 
Lezione12: Autenticazione e gestione delle sessioni in REST
Lezione12: Autenticazione e gestione delle sessioni in RESTLezione12: Autenticazione e gestione delle sessioni in REST
Lezione12: Autenticazione e gestione delle sessioni in RESTAndrea Della Corte
 

Was ist angesagt? (20)

Lezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in JavaLezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in Java
 
Introduzione a Git
Introduzione a GitIntroduzione a Git
Introduzione a Git
 
Git basics to advance with diagrams
Git basics to advance with diagramsGit basics to advance with diagrams
Git basics to advance with diagrams
 
Spring boot
Spring bootSpring boot
Spring boot
 
Inheritance C#
Inheritance C#Inheritance C#
Inheritance C#
 
Git training v10
Git training v10Git training v10
Git training v10
 
Java Spring framework, Dependency Injection, DI, IoC, Inversion of Control
Java Spring framework, Dependency Injection, DI, IoC, Inversion of ControlJava Spring framework, Dependency Injection, DI, IoC, Inversion of Control
Java Spring framework, Dependency Injection, DI, IoC, Inversion of Control
 
Git - Level 2
Git - Level 2Git - Level 2
Git - Level 2
 
Bootstrap 4 ppt
Bootstrap 4 pptBootstrap 4 ppt
Bootstrap 4 ppt
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
 
POO - Aula 09 - Herança
POO - Aula 09 - HerançaPOO - Aula 09 - Herança
POO - Aula 09 - Herança
 
Introduction to Spring WebFlux #jsug #sf_a1
Introduction to Spring WebFlux #jsug #sf_a1Introduction to Spring WebFlux #jsug #sf_a1
Introduction to Spring WebFlux #jsug #sf_a1
 
SPRING - MAVEN - REST API (ITA - Luglio 2017)
SPRING - MAVEN - REST API (ITA - Luglio 2017)SPRING - MAVEN - REST API (ITA - Luglio 2017)
SPRING - MAVEN - REST API (ITA - Luglio 2017)
 
Introdução APIs RESTful
Introdução APIs RESTfulIntrodução APIs RESTful
Introdução APIs RESTful
 
Adobe AEM core components
Adobe AEM core componentsAdobe AEM core components
Adobe AEM core components
 
Java OCA teoria 1
Java OCA teoria 1Java OCA teoria 1
Java OCA teoria 1
 
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorReactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
 
git and github
git and githubgit and github
git and github
 
JavaScript JQUERY AJAX
JavaScript JQUERY AJAXJavaScript JQUERY AJAX
JavaScript JQUERY AJAX
 
Lezione12: Autenticazione e gestione delle sessioni in REST
Lezione12: Autenticazione e gestione delle sessioni in RESTLezione12: Autenticazione e gestione delle sessioni in REST
Lezione12: Autenticazione e gestione delle sessioni in REST
 

Ähnlich wie Programmazione funzionale e Stream in Java

Programmazione Funzionale per tutti
Programmazione Funzionale per tuttiProgrammazione Funzionale per tutti
Programmazione Funzionale per tuttiSalvatore Sorrentino
 
Scala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéScala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéEdmondo Porcu
 
Introduzione a scala prima parte
Introduzione a scala   prima parteIntroduzione a scala   prima parte
Introduzione a scala prima parteOnofrio Panzarino
 
DotNetToscana - Sessione TypeScript
DotNetToscana - Sessione TypeScriptDotNetToscana - Sessione TypeScript
DotNetToscana - Sessione TypeScriptSinergia Totale
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptLuca Pagliaro
 
Programmazione a oggetti tramite la macchina del caffé (pt. 3)
Programmazione a oggetti tramite la macchina del caffé (pt. 3)Programmazione a oggetti tramite la macchina del caffé (pt. 3)
Programmazione a oggetti tramite la macchina del caffé (pt. 3)Marcello Missiroli
 
Corso Python Deltapromo lezione 1
Corso Python Deltapromo   lezione 1Corso Python Deltapromo   lezione 1
Corso Python Deltapromo lezione 1Paolo Ferretti
 
Lezione 6a: Design Pattern Strutturali
Lezione 6a: Design Pattern StrutturaliLezione 6a: Design Pattern Strutturali
Lezione 6a: Design Pattern StrutturaliAndrea Della Corte
 
Repository pattern slides v1.1
Repository pattern slides v1.1Repository pattern slides v1.1
Repository pattern slides v1.1Christian Nastasi
 
Introduzione a TypeScript
Introduzione a TypeScriptIntroduzione a TypeScript
Introduzione a TypeScriptSinergia Totale
 
Elaborazione automatica dei dati: computer e matlab
Elaborazione automatica dei dati: computer e matlabElaborazione automatica dei dati: computer e matlab
Elaborazione automatica dei dati: computer e matlabprofman
 
Sviluppare su OpenOffice.org con Java
Sviluppare su OpenOffice.org con JavaSviluppare su OpenOffice.org con Java
Sviluppare su OpenOffice.org con JavaMarcello Teodori
 

Ähnlich wie Programmazione funzionale e Stream in Java (20)

Programmazione Funzionale per tutti
Programmazione Funzionale per tuttiProgrammazione Funzionale per tutti
Programmazione Funzionale per tutti
 
Scala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéScala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perché
 
Introduzione a scala prima parte
Introduzione a scala   prima parteIntroduzione a scala   prima parte
Introduzione a scala prima parte
 
DotNetToscana - Sessione TypeScript
DotNetToscana - Sessione TypeScriptDotNetToscana - Sessione TypeScript
DotNetToscana - Sessione TypeScript
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScript
 
Programmazione a oggetti tramite la macchina del caffé (pt. 3)
Programmazione a oggetti tramite la macchina del caffé (pt. 3)Programmazione a oggetti tramite la macchina del caffé (pt. 3)
Programmazione a oggetti tramite la macchina del caffé (pt. 3)
 
Corso Python Deltapromo lezione 1
Corso Python Deltapromo   lezione 1Corso Python Deltapromo   lezione 1
Corso Python Deltapromo lezione 1
 
Lezione01
Lezione01Lezione01
Lezione01
 
Lezione01
Lezione01Lezione01
Lezione01
 
Lezione 6a: Design Pattern Strutturali
Lezione 6a: Design Pattern StrutturaliLezione 6a: Design Pattern Strutturali
Lezione 6a: Design Pattern Strutturali
 
Repository pattern slides v1.1
Repository pattern slides v1.1Repository pattern slides v1.1
Repository pattern slides v1.1
 
introduzione a symfony 2
introduzione a symfony 2 introduzione a symfony 2
introduzione a symfony 2
 
Introduzione a TypeScript
Introduzione a TypeScriptIntroduzione a TypeScript
Introduzione a TypeScript
 
Elaborazione automatica dei dati: computer e matlab
Elaborazione automatica dei dati: computer e matlabElaborazione automatica dei dati: computer e matlab
Elaborazione automatica dei dati: computer e matlab
 
Intro to JavaScript
Intro to JavaScriptIntro to JavaScript
Intro to JavaScript
 
Corso ABAP OO 01
Corso ABAP OO   01Corso ABAP OO   01
Corso ABAP OO 01
 
Inferno Limbo Italian
Inferno Limbo ItalianInferno Limbo Italian
Inferno Limbo Italian
 
Ruby in 25 minuti
Ruby in 25 minutiRuby in 25 minuti
Ruby in 25 minuti
 
Sviluppare su OpenOffice.org con Java
Sviluppare su OpenOffice.org con JavaSviluppare su OpenOffice.org con Java
Sviluppare su OpenOffice.org con Java
 
Java 8
Java 8Java 8
Java 8
 

Programmazione funzionale e Stream in Java

  • 1. FUNCTIONAL PROGRAMMING & JAVA Cenni di programmazione Funzionale e Java Optional e Stream
  • 4. FP • Un linguaggio può favorire uno stile di programmazione rispetto ad un altro, ma nulla di più. • Alcuni problemi sono più facilmente risolvibili con un approccio rispetto che con un altro. • Le nuove implementazioni in Java8 rendono più semplice scrivere parti usando un approccio funzionale, ma era possibile anche prima (con librerie esterne come Guava). • Javascript consente un approccio event driven così come un approccio funzionale. • Javascript è un linguaggio non tipizzato (loosely typed) e questo lo rende estremamente flessibile, quindi anche l’approccio funzionale è del tutto possibile.Tuttavia è abbastanza buffo se si considera che i linguaggi pensati per un approccio funzionale hanno solitamente un forte controllo sui tipi per fornire un aiuto nella rilevazione di eventuali errori. 4
  • 5. FP INTIME • Si basa sul Lambda Calculus, sviluppato nel 1930 • LISP è stato il primo linguaggio funzionale (1960) • Negli anni ’70 è stato sviluppato ML (MetaLanguage) presso l’università di Edimburgo 5 [https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-1-1f15e387e536#.muqfaya41]
  • 6. IN MATEMATICA: FUNZIONI • In matematica, una funzione è una relazione tra due insiemi, chiamati dominio e codominio della funzione, che associa ad ogni elemento del dominio uno e un solo elemento del codominio. • Se i due insiemi sono rispettivamente indicati con X eY, la relazione è indicata con f:X⟶Y e l'elemento associato a x ∈ X tramite la funzione f viene abitualmente indicato con f(x) (si pronuncia "effe di x”). 6 [Wikipedia]
  • 7. COMPOSIZIONE DI FUNZIONI In matematica, la composizione di funzioni è l'applicazione di una funzione al risultato di un'altra funzione. Più precisamente, una funzione f tra due X eY trasforma ogni elemento di X in uno diY: in presenza di un'altra funzione g che trasforma ogni elemento diY in un elemento di un altro insieme Z, si definisce la composizione di f e g come la funzione che trasforma ogni elemento di X in uno diY usando prima f e poi g. Il simbolo Unicode dell'operatore è ∘ (U+2218). (Wikipedia) • Questo significa che si possono comporre solo funzioni il cui codominio di una sia il dominio della successiva che verrà applicata (quindi il controllo sui tipi aiuta!). • Nei linguaggi di programmazione che consideriamo le funzioni ritornano un solo valore (che ovviamente può essere di qualsiasi tipo: numero, stringa, array, oggetto, funzione…) Quindi ci troveremo a comporre funzioni con un solo argomento come parametro. • g∘f (x) = g(f(x)) quindi viene prima applicato f e poi g. 7 [Wikipedia]
  • 8. FP:ALCUNI ASPETTI • Le funzioni sono first class members • Pure functions • Immutabilità • Function composition • Currying • Functor e Monad (either, optional, Java stream) 8
  • 9. FIRST CLASS MEMBERS • in un linguaggio funzionale, le funzioni sono un tipo di dati esattamente come gli altri (un numero, un testo). Quindi possono essere passate come parametro ad un metodo o essere restituite come output di un altro. • in Java8 questo è possibile, ma a volte è abbastanza prolisso (l’inferenza dei tipi ha ancora limiti, è necessario un uso esteso dei generici, e c’è la necessità di dichiarare tipi se le interfacce funzionali già a disposizione non sono sufficienti) • in JavaScript invece la cosa è del tutto normale 9 Unnecessary function wrapping: in Javascript è totalmente evitabile, in Java relativamente alle possibilità offerte dal linguaggio [https://github.com/MostlyAdequate/mostly-adequate-guide]
  • 10. PURE FUNCTIONS “A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect. […] A side effect is a change of system state or observable interaction with the outside world that occurs during the calculation of a result.” (Mostly Adequate Guide to Functional Programming) Quindi una funzione che non dipende dallo stato di variabili esterne e che non altera i dati (in JS ad esempio array.splice che modifica l’array vs array.slice che torna una shallow copy modificata), non accede al file system o a un database… Ovviamente, un software per poter funzionare, provocherà inevitabilmente delle alterazioni all’esterno, l’obiettivo è relegare e minimizzare le parti di codice impuro. 10
  • 11. PURE FUNCTIONS WHY? • Portable (self explanatory) • Testable • Referential transparency (un blocco di codice che può essere sostituito con il suo valore valutato senza che questo modifichi il comportamento del programma) • Parallel code 11
  • 12. IMMUTABILITÀ • Nei linguaggi di programmazione puramente funzionali non esiste il concetto di “variabile”, quando un valore viene assegnato ad un parametro x, questo rimane immutabile per tutta la durata della vita di x. • Quindi il concetto di loop non esiste in programmazione funzionale pura (su cosa faccio il loop se non ho le variabili?!?). • Ma, senza variabili? Si compongono funzioni. (in ogni caso nei linguaggi funzionali si possono definire dei parametri per migliorare la leggibilità, solo che una volta che gli è stato assegnato il valore, non è più possibile mutarlo) • Ma, senza i loop? Dipende dal caso, o con la ricorsione o sfruttando le funzionalità offerte in altro modo sulle collezioni di item. 12
  • 13. CURRYING 13 Dicevamo che la composizione di funzioni si può fare solo su funzioni di un solo parametro. E per funzioni che richiedono più parametri? “In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument. Currying provides a way for working with functions that take multiple arguments, and using them in frameworks where functions might take only one argument” (Wikipedia) Questo avviene in Javascript solitamente attraverso delle librerie aggiuntive. Chiamando una funzione con meno argomenti di quelli che richiede, si ottiene una nuova funzione che richiede gli elementi rimanenti. Si chiama così in onore di Haskell Curry.
  • 14. FUNCTOR • Esiste una branca della matematica: Category theory, che cerca di unificare altre branche (l’idea di base è che una categoria consiste in oggetti e funzioni). Chi ha sviluppato i primi linguaggi di programmazione funzionali si è ispirato a concetti dalla teoria delle categorie. Un concetto interessante è quello del Functor. • Per quanto ci riguarda l’idea è quella di avere un contenitore, in grado di contenere una proprietà. Quando la proprietà viene messa nel contenitore, ci resta. Se il nostro contenitore si chiama C, il suo metodo of() crea un contenitore C con all’interno il valore passato. Possiamo tirarla fuori, ma ha senso farlo solo in particolari momenti. La sua utilità sta nel poter operare su C senza sapere necessariamente cosa c’è dentro. • Una volta che la proprietà è nel contenitore, vogliamo applicare su di essa delle funzioni: ecco il metodo map(). • A Functor is a type that implements map and obey some laws • E tanto basta, altrimenti ci si può avventurare nella CategoryTheory (per alcuni link vedi più avanti) 14 [https://github.com/MostlyAdequate/mostly-adequate-guide]
  • 15. MONAD • Allo stesso modo si può sentir parlare di monadi, per il momento ci basta tenere presente questa definizione a grandi linee: • “Any functor which defines a join method, has an of method, and obeys a few laws is a monad. ” • Il metodo join si occupa di appiattire: dato che invocare map() oppure of() più volte può generare una proliferazione di contenitori che contengono contenitori che contengono contenitori… serve un modo per appiattire il tutto: in pseudo codice l’idea è C(C(C(x))).join() = C(C(x)) 15
  • 16. FP IN JAVASCRIPT • Abbiamo detto che esistono librerie che rendono possibile e semplice la programmazione funzionale in JS. Fantasyland specs mira a fornire le specifiche che tali librerie devono implementare (per i più avventurosi c’è il link più avanti). • JS fornisce nativamente alcune funzionalità, tramite funzioni già implementate. Si pensi a Array.map(): 16 • Per altre funzionalità si possono utilizzare librerie come lodash-fp o ramdajs. Tipicamente: • Null-check problem: la monade Maybe può essere usata, dato consente di concatenare vari metodi map in modo da applicare la funzione solo se il contenuto di Maybe è un valore, altrimenti viene tornata la Monade Maybe.of(null) • La gestione delle eccezioni con la possibilità di terminare subito dopo un errore: questo può essere gestito tramite la monade Either, che ha due costruttori: Either.Left e Either.Right. L’idea è quella di memorizzare l’errore in Left e i valori utili in Right. Sia Left che Right implementano map ma in modo da propagare e conservare l’errore (un po’ come null nella monade Maybe)
  • 17. FP IN DELPHI • In Delphi data la facilità con cui si possono definire nuovi tipi, è molto semplice dichiarare che un metodo (function o procedure) accetta come parametro un altro metodo. • In ogni caso, il pas System.SysUtils contiene un insieme di dichiarazioni utili di tipi di metodi. • Con il Delphi 2010 sono stati introdotti i metodi anonimi, sono metodi che possono essere scritti inline per essere passati come parametro in un altro metodo, rimangono prolissi, ma possono risultare utili 17
  • 18. FP GOOD READINGS • Articoli introduttivi: https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer- part-1-1f15e387e536#.rqaf0fx53 e https://medium.freecodecamp.org/functional-programming-in-js-with- practical-examples-part-1-87c2b0dbc276 • Fantasyland spec: https://github.com/fantasyland/fantasy-land • Professor Frisby’s “Mostly adequate guide to Functional Programming” (https://github.com/ MostlyAdequate/mostly-adequate-guide) divertente e accurato, analizza JavaScript, ma i concetti sono interamente applicabili aTypeScript e a Java e Delphi in una certa misura • You don’t know JavaScript (https://github.com/getify/You-Dont-Know-JS) Riguarda solo JS, solo per chi ama sapere perché le cose funzionano come funzionano. • Category Theory https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the- preface/, o in alternativa la sua serie di video https://www.youtube.com/watch?v=I8LbkfSSR58 • Video sulla programmazione funzionale in Delphi https://www.youtube.com/watch?v=HDhmUjzUNyQ 18
  • 20. UN PASSO INDIETRO • Gli Stream, java.util.function, Optional, lambda expression, method reference…sono un insieme di strumenti • rendono possibile un approccio funzionale a (certi) problemi 20
  • 21. AGGIUNTE:A COLLECTION 21 1 package java.util; 2 3 public interface Collection<E> extends Iterable<E> { 4 // ... 24 default boolean removeIf(Predicate<? super E> filter) { 25 Objects.requireNonNull(filter); 26 boolean removed = false; 27 final Iterator<E> each = iterator(); 28 while (each.hasNext()) { 29 if (filter.test(each.next())) { 30 each.remove(); 31 removed = true; 32 } 33 } 34 return removed; 35 } 36 } 396 private static void validatePendingConfigurations() { 397 final Set<String> toBeRemoved = Sets.newHashSet(); 398 final ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); 399 400 for (final String configuration : pendingConfigurations) { 401 if (null == classLoader.getResourceAsStream(configuration)) { 402 log.info("Removing a configuration: " + configuration); 403 toBeRemoved.add(configuration); 405 } 406 } 407 408 for (final String configuration : toBeRemoved) { 409 pendingConfigurations.remove(configuration); 410 } 411 } 396 private static void validatePendingConfigurations() { 397 final ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); 398 pendingConfigurations.removeIf(c -> 399 null == classLoader.getResourceAsStream(configuration)); 400 }
  • 22. AGGIUNTE:A ITERATOR • In alcuni casi ho a disposizione solo un Iterator, quindi non posso usare Collection.forEach • Ad esempio AbstractIndexedLayout.getComponents() • In questi casi si può usare Iterator.forEachRemaining 22 1 package java.util; 2 3 import java.util.function.Consumer; 4 5 /** 6 * ... 7 */ 8 public interface Iterator<E> { 9 10 11 /** 12 * Performs the given action for each remaining element until all 13 elements 14 * have been processed or the action throws an exception. Actions are 15 * performed in the order of iteration, if that order is specified. 16 * Exceptions thrown by the action are relayed to the caller. 17 * 18 * @implSpec 19 * <p>The default implementation behaves as if: 20 * <pre>{@code 21 * while (hasNext()) 22 * action.accept(next()); 23 * }</pre> 24 * 25 * @param action The action to be performed for each element 26 * @throws NullPointerException if the specified action is null 27 * @since 1.8 28 */ 29 default void forEachRemaining(Consumer<? super E> action) { 30 Objects.requireNonNull(action); 31 while (hasNext()) 32 action.accept(next()); 33 } 34 }
  • 23. • Vedi la presentazione su Java8 • Tutto il package java.util.function contiene le interfacce e metodi estremamente utili, sia nell’uso all’interno degli Stream che per un approccio funzionale. Va esplorato (Function.compose(), Predicate.and()…) • Voglio trasformare una lista di oggetti X in una mappa con in chiave gli stessi oggetti? ho il metodo UnaryOperatoridentity • Voglio filtrare solo gli oggetti nulli o solo i non nulli? ecco ObjectsnonNull e Objectsnull • Voglio filtrare una collezione di oggetti recuperando solo quelli di classe AClass, e ritornare una List<AClass>? (E non è un’aggiunta da Java 8, ma qui tornano comodi…) Esistono AClassisInstance e AClasscast INSOMMA:AGGIUNTE!
  • 24. OPTIONAL • Rappresenta più o meno la monade Maybe: • è un contenitore che può contenere un valore, o null. Ha quindi i metodi per ottenere un Optional da un valore o da null • ha il metodo map che trasforma il suo contenuto se è diverso da null, altrimenti torna Optional.ofNull • è generico rispetto a cosa contiene: Optional<Integer> o = Optional.of(3) • è stato introdotto principalmente per lavorare con gli Stream • ha altri metodi utili come ad esempio filter: ritorna l’optional stesso se il valore all’interno verifica il predicato, altrimenti ritorna l’optional di null. 24
  • 25. JAVA STREAM L’astrazione principale introdotta è lo stream, (in generale uno stream è la coppia elemento n-simo e la modalità con cui generare il successivo). Sono generici nel tipo del loro contenuto. Gli stream sono diversi dalle collezioni per vari motivi: • è funzionale, (nel senso di FP) quindi operazioni sugli stream producono un risultato, ma non vanno a modificare la sorgente dello stream • è intrinsecamente lazy, le operazioni sugli stream sono divise in due tipi: intermedie (filter, map…) e terminali (collect, sum…). Le operazioni intermedie sono sempre lazy. • uno stream non ha necessariamente una fine • Gli elementi di uno stream sono visitati una sola volta durante la vita di uno stream, per visitarli di nuovo, va creato un nuovo stream
  • 26. • Ci sono vari metodi per ottenere uno stream, quello tipico è Collection.stream(), e anche Arrays.stream(). Ci sono inoltre i metodi statici delle varie classi Stream o IntStream… • Le operazioni sugli stream sono divise in intermedie e terminali e combinate assieme in uno stream pipeline che è quindi sempre composto da: • uno stream sorgente, • 0 o più operazioni intermedie ed • una operazione terminale. • Le operazioni intermedie sono quelle che ritornano un nuovo stream. Sono sempre lazy nel senso che quando ad esempio si esegue un operazione di filtro con il metodo filter() questa non esegue il filtro subito, ma crea un nuovo stream che, una volta attraversato, contiene solo gli elementi dello stream originale che verificano la condizione del filtro. • Le operazioni terminali possono visitare tutto o parte dello stream e/ o produrre un side effect. Dopo che l’operazione terminale è stata eseguita lo stream è consumato e non più riutilizzabile (nella quasi totalità dei casi).
  • 27. PER ESEMPIO (1) [http://files.zeroturnaround.com/pdf/zt_java8_streams_cheat_sheet.pdf] Operazioni intermedie • filter: ritorna uno stream con i soli elementi che soddisfano il predicato passato • map: trasforma lo stream in un nuovo stream contenente il risultato della funzione passata (applicata sugli elementi dello stream originale) • distinct: ritorna uno stream di elementi unici nello stream • limit: ritorna uno stream che contiene solo il numero di elementi passati Operazioni terminali • reduce: serve ad accumulare sugli elementi dello stream • collect: ritorna una collezione (List, Set…) composta dagli elementi dello stream • forEach: per eseguire operazioni che hanno un effetto secondario sugli elementi 27
  • 28. • Le operazioni intermedie si possono dividere in due sottotipi: stateless e stateful (come distinct e sorted). Le operazioni di tipo stateful hanno bisogno dello stato dell’elemento precedente dello stream, inoltre possono aver bisogno di processare l’intero stream prima di produrre un risultato. • Gli stream facilitano l’esecuzione parallela del codice, ma in questa sede non approfondiamo, l’unica cosa che vale la pena ricordare: side-effects prodotti dentro ad uno stream possono portare a problemi di thread-safety. Quindi cercare di evitare il più possibile cose del genere: [Stream Javadoc] [Stream Javadoc]
  • 29. INTERMEDIE • Abbiamo detto che le operazioni intermedie sono lazy nel senso che tutte producono un nuovo stream. • Proprio per questo l’ordine in cui concateno le operazioni intermedie è estremamente importante: • se possibile, le operazioni di filter vanno messe per prime, dato che fanno diminuire il numero di elementi di input che proseguono lungo la pipeline 29
  • 30. INTERMEDIE: FLATMAP • FlatMap serve nel caso in cui si stia manipolando un insieme di oggetti che a loro volta contengono delle collezioni da manipolare, l’idea è che da ogni elemento dello stream originale si produca un una serie di stream che verranno accodati assieme. 30
  • 31. TERMINALI: COLLECT • Collect trasforma gli elementi dello Stream in un tipo diverso di risultato. • Java 8 fornisce la classe Collectors (toSet(), toMap()…) • è comunque possibile implementare un proprio collector 31 [http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/]
  • 32. TERMINALI: REDUCE Reduce combina tutti gli elementi di uno stream in un unico risultato. Sono supportati tre tipi di reduce. • Optional<T> reduce(Bifunction<T> accumulator) • T reduce(T identity, BinaryOperator<T> accumulator) 32 [http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/] • <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner); La terza firma di reduce si utilizza (e il combiner viene effettivamente invocato) se si eseguono gli stream in parallelo. L’idea è che il combiner viene eseguito quando arriva il momento di rimettere assieme le varie parti eseguite in parallelo.
  • 33. UTILI • IntStream.range(startIncluded, endExcluded) e IntStream.of al posto di un ciclo for. • Esistono gli stream di primitive: IntStream, LongStream e DoubleStream. Essi supportano particolari funzioni terminali come sum() e average(). Inoltre ciascuno ha il metodo boxed() per passare allo Stream corrispondente: IntStream.boxed() produce uno Stream<Integer>. Il contrario può essere fatto con i metodi mapToInt(), mapToLong() e mapToDouble(). • Da array a stream e viceversa: Arrays.stream() e Stream.toArray() • La concatenazione di stream: Stream.concat() o Stream.of() • Non è possibile riutilizzare uno stesso Stream, ma si può usare Supplier: • A volte non serve usare gli stream, se si vuole semplicemente iterare su una Collection si può utilizzare forEach() • Collectors (toList, toMap, groupingBy e un Collector ad hoc) 33 [http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/]
  • 34. FP O IMPERATIVE? Di base va tenuto presente un unico concetto: è poco leggibile (a volte pericoloso) e poco sensato mescolare programmazione imperativa e funzionale. Se si sta scrivendo un algoritmo usando uno stile, si deve cercare di evitare di usare simultaneamente anche l’altro (quanto meno chiedersi e investigare se si può fare meglio) • Le IDE danno ottimi suggerimenti sul come migliorare il codice, analizziamo i warning che danno: • Ad esempio: usiamo i reference method, non stream.map(placeplace.getName()) ma stream.map(PlacegetName) • Non stream().xxx.collect(toList()).toArray(new String[]{}) ma stream().xxx.toArray(String[]new) Terminale Stream sorgente
  • 35. PER ESEMPIO (2) Diventa, evitando di passare da funzionale a imperativo: Torna un Optional<Filter> Torna un Optional<Boolean>
  • 36. PER ESEMPIO (3) 161 protected boolean doDraw() { 162 placesTableLayout.removeAllComponents(); 163 Set<LocalGeodesy> currentSource = getCurrentSourceValues(); 164 if (!currentSource.isEmpty()) { 165 double totalLength = currentSource.stream() 166 .filter(geodesy -> geodesy instanceof LocalPath) 167 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum(); 168 if (totalLength != 0.0d) { 169 double controlledLength = currentSource.stream() 170 .filter(geodesy -> geodesy instanceof LocalPath 171 && PlannerMapFilterLayout.CONTROLLED.equals(geodesy.getColor())) 172 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum(); 173 double needControlLength = currentSource.stream() 174 .filter(geodesy -> geodesy instanceof LocalPath 175 && NEED_CONTROL.equals(geodesy.getColor())) 176 .mapToDouble(geodesy -> ((LocalPath) geodesy).getLength()).sum(); 177 double expiredLength = totalLength - controlledLength - needControlLength; 178 long numOfControlledPlaces = currentSource.stream() 179 .filter(geodesy -> CONTROLLED.equals(geodesy.getColor())) 180 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count(); 181 long numOfNeedControlPlaces = currentSource.stream() 182 .filter(geodesy -> NEED_CONTROL.equals(geodesy.getColor())) 183 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count(); 184 long numOfExpiredPlaces = currentSource.stream() 185 .filter(geodesy -> EXPIRED.equals(geodesy.getColor())) 186 .mapToLong(geodesy -> geodesy.getPlace().getId()).distinct().count(); 187 Chart chart = getMetersControlledPie(numOfControlledPlaces, controlledLength, 188 numOfNeedControlPlaces, needControlLength, numOfExpiredPlaces, 189 expiredLength); 190 chart.setHeight("150px"); 191 placesTableLayout.addComponent(chart); 192 } 193 updateNeedControlPlacesTable(currentSource); 194 placesTableLayout.addComponent(needControlTable); 195 placesTableLayout.addComponent(involvedPlacesTable); 196 placesTableLayout.setExpandRatio(involvedPlacesTable, 1.0f); 197 placesTableLayout.setExpandRatio(needControlTable, 1.0f); 198 } 199 return super.doDraw(); 200 }
  • 37. …DIVENTA 83 protected static double getLengthOf(Predicate<LocalGeodesy> predicate, Set<LocalGeodesy> geodesies) { 84 if (predicate == null) { 85 predicate = g -> true; 86 } 87 return geodesies.stream() 88 .filter(predicate.and(LocalPath.class::isInstance)) 89 .map(LocalPath.class::cast) 90 .mapToDouble(LocalPath::getLength).sum(); 91 } 92 93 protected static long countIf(Predicate<LocalGeodesy> predicate, Set<LocalGeodesy> geodesies) { 94 if (predicate == null) { 95 predicate = g -> true; 96 } 97 return geodesies.stream() 98 .filter(predicate) 99 .mapToLong(geodesy -> geodesy.getPlace().getId()) 100 .distinct().count(); 101 } 127 static final Predicate<LocalGeodesy> isExpired = geodesy -> EXPIRED.equals(geodesy.getColor()); 128 static final Predicate<LocalGeodesy> isControlled = geodesy -> CONTROLLED.equals(geodesy.getColor()); 129 static final Predicate<LocalGeodesy> needsControl = geodesy -> NEED_CONTROL.equals(geodesy.getColor()); 153 @Override 154 protected boolean doDraw() { 155 placesTableLayout.removeAllComponents(); 156 Set<LocalGeodesy> currentSource = getCurrentSourceValues(); 157 if (!currentSource.isEmpty()) { 158 159 double totalLength = getLengthOf(null, currentSource); 160 if (totalLength != 0.0d) { 161 double controlledLength = getLengthOf(isControlled, currentSource); 162 double needControlLength = getLengthOf(needsControl, currentSource); 163 164 double expiredLength = totalLength - controlledLength - needControlLength; 165 166 long numOfControlledPlaces = countIf(isControlled, currentSource); 167 long numOfNeedControlPlaces = countIf(needsControl, currentSource); 168 long numOfExpiredPlaces = countIf(isExpired, currentSource); 169 170 Chart chart = getPieChart(numOfControlledPlaces, controlledLength, 171 numOfNeedControlPlaces, needControlLength, numOfExpiredPlaces, 172 expiredLength, anyMatch(LocalCoordinates.class::isInstance, currentSource.stream())); 173 chart.setHeight("250px"); 174 placesTableLayout.addComponent(chart); 175 } 176 updateNeedControlPlacesTable(currentSource); 177 placesTableLayout.addComponent(needControlTable); 178 placesTableLayout.addComponent(involvedPlacesTable); 179 placesTableLayout.setExpandRatio(involvedPlacesTable, 1.0f); 180 placesTableLayout.setExpandRatio(needControlTable, 1.0f); 181 } 182 return super.doDraw(); 183 }
  • 38. JAVA 8 E FP: LIMITAZIONI • Non esiste un’implementazione di Either • Optional è stata introdotta solo dopo molte discussioni e solo per il suo uso per gli Stream, viene consigliato di non usarla al di fuori di quel contesto • Le varie interfacce in java.util.function non appartengono ad una stessa gerarchia comune, quindi non si riescono a comporre (in senso matematico) dato che solo Function ha i metodi compose e andThen • Tuttavia le lambda possono diventare qualunque cosa (nei limiti del loro tipo), basta forzare la type inference: .filter(((Predicate<ThingMO>) this::isInlet).negate()) 38
  • 39. JAVA GOOD READINGS • http://www.studytrails.com/java/java8/ Java8_Lambdas_FunctionalProgramming.jsp • https://zeroturnaround.com/rebellabs/java-8-streams-cheat-sheet/ • https://dzone.com/articles/introduction-writing-custom • http://www.nurkiewicz.com/2014/07/introduction-to-writing- custom.html • http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 39