Diciannovesima lezione

Cosa sono le espressioni Lambda

Lo stile di programmazione imperativo (sequenza di comandi 'compilata' e fissa) a cui siamo stati abituati fin qui non è l'unico possibile. Può essere sostituito in modo equivalente con uno stile in cui per ogni operazione viene costruita in modo dinamico una funzione apposita e 'anonima' che contiene le operazioni da fare ed i loro argomenti. Questo stile di programmazione funzionale è alla base del linguaggio LISP dal lontano 1958. Ad esempio, le direttive per stampare il coseno di 3.14 in LISP sono:
(write-line (write-to-string (cos 3.14)))
Il concetto di legare le funzioni alle variabili viene utilizzato anche in un sistema logico-matematico formale detto Lambda calculus.
Non è quindi un'idea nuova, ma sta avendo una esplosione di popolarità negli anni 201x:, anche nel C++:

Lambda expressions - lambdas - are a game changer in C++ programming. That's somewhat surprising, because they bring no expressive power to the language. Everything a lambda can do is something you can do by hand with a bit more typing. But lambdas are such a convenient way to create function objects, the impact on day-to-day C++ software development is enormous.
Scott Meyers, Effective Modern C++, Chapter 6.

Cerchiamo quindi di capire come funziona e quali sono i suoi vantaggi pratici.

Il primo valore aggiunto delle espressioni lambda in C++ è facilitare, ed in qualche modo rendere preferibile, l'uso degli algoritmi della libreria standard. Proviamo a confrontare con gli esempi ancora presenti in cplusplus.com questo

La Closure

Le espressioni lambda consentono di creare a runtime delle closure, oggetti funzione che possono anche contenere valori o reference a variabili presenti nello scope in cui vengono creati. Per poter essere inclusa in una closure una variabile deve essere:
  1. Locale e accessibile nella funzione che contiene l'espressione lambda (anche un parametro della funzione).
  2. Non static.
  3. Non un data member della classe a cui la funzione appartiene.

L'espressione fra parentesi quadre indica in che modo devono essere catturate le variabili:

Ad esempio:

Attenzione: se si costruisce una closure con reference a variabili locali per una operazione che non viene eseguita immediatamente, la closure si troverà a contenere dangling references.

Uso nella programmazione asincrona

Il solo fatto di richiedere meno sforzo alla tastiera, o di produrre codice più leggibile perchè non costringe a cercare la definizione di funzioni brevi lontano da dove vengono utilizzate non basta da solo a spiegare le ragioni della loro introduzione. Neanche basta a giustificarle il fatto che il popolarissimo linguaggio Javascript le includa fra le sue poche caratteristiche essenziale.

Le lambda function rendono in qualche modo accessibile un modello di programmazione dell'I/O non sincrono/sequenziale, non basato su 'event loop', non basato su programmazione propriamente parallela (thread), ma task-based. Le varie operazioni in attesa vengono accodate e gestite quando sono pronte. Al loro completamento (con o senza errore) viene invocata una opportuna funzione di call-back.

La libreria boost::asio permette di realizzare un simile modello di I/O asincrono, e anche in questo caso ci si può avvantaggiare delle espressioni Lambda.

Il sito di Boost offre tutorial per la creazione sia di clienti TCP che di server TCP sincroni e asincroni, ma si limita a fornire esempi C++98. Commentiamo ora questo , che implementa un cliente HTTP asincrono attraverso una cascata di callback.

Esercizio: Provare ad implementare una comunicazione TCP con boost::asio.